diff --git a/JellyfinPlayer tvOS/Components/LandscapeItemElement.swift b/JellyfinPlayer tvOS/Components/LandscapeItemElement.swift index 49adce26..d638e022 100644 --- a/JellyfinPlayer tvOS/Components/LandscapeItemElement.swift +++ b/JellyfinPlayer tvOS/Components/LandscapeItemElement.swift @@ -43,7 +43,7 @@ struct LandscapeItemElement: View { var body: some View { VStack { - ImageView(src: (item.type == "Episode" ? item.getSeriesBackdropImage(maxWidth: 800) : item.getBackdropImage(maxWidth: 800)), bh: item.type == "Episode" ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash()) + ImageView(src: (item.type == "Episode" ? item.getSeriesBackdropImage(maxWidth: 445) : item.getBackdropImage(maxWidth: 445)), bh: item.type == "Episode" ? item.getSeriesBackdropImageBlurHash() : item.getBackdropImageBlurHash()) .frame(width: 445, height: 250) .cornerRadius(10) .overlay( diff --git a/JellyfinPlayer tvOS/Components/MediaViewActionButton.swift b/JellyfinPlayer tvOS/Components/MediaViewActionButton.swift new file mode 100644 index 00000000..ff1573ad --- /dev/null +++ b/JellyfinPlayer tvOS/Components/MediaViewActionButton.swift @@ -0,0 +1,37 @@ +// + /* + * SwiftFin is subject to the terms of the Mozilla Public + * License, v2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * Copyright 2021 Aiden Vigue & Jellyfin Contributors + */ + +import SwiftUI + +struct MediaViewActionButton: View { + @Environment(\.isFocused) var envFocused: Bool + @State var focused: Bool = false + var icon: String + @Binding var scrollView: UIScrollView? + var iconColor: Color? + + var body: some View { + Image(systemName: icon) + .foregroundColor(focused ? .black : iconColor ?? .white) + .onChange(of: envFocused) { envFocus in + if(envFocus == true) { + scrollView?.scrollToTop() + DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { + scrollView?.scrollToTop() + } + } + + withAnimation(.linear(duration: 0.15)) { + self.focused = envFocus + } + } + .font(.system(size: 40)) + .padding(.vertical, 12).padding(.horizontal, 20) + } +} diff --git a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift index 86c0095d..774b8a13 100644 --- a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift +++ b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift @@ -19,7 +19,7 @@ struct PortraitItemElement: View { var body: some View { VStack { - ImageView(src: item.type == "Episode" ? item.getSeriesPrimaryImage(maxWidth: 400) : item.getPrimaryImage(maxWidth: 400), bh: item.type == "Episode" ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash()) + ImageView(src: item.type == "Episode" ? item.getSeriesPrimaryImage(maxWidth: 200) : item.getPrimaryImage(maxWidth: 200), bh: item.type == "Episode" ? item.getSeriesPrimaryImageBlurHash() : item.getPrimaryImageBlurHash()) .frame(width: 200, height: 300) .cornerRadius(10) .shadow(radius: focused ? 10.0 : 0) diff --git a/JellyfinPlayer tvOS/LatestMediaView.swift b/JellyfinPlayer tvOS/LatestMediaView.swift index 247586a5..8363027d 100644 --- a/JellyfinPlayer tvOS/LatestMediaView.swift +++ b/JellyfinPlayer tvOS/LatestMediaView.swift @@ -42,9 +42,11 @@ struct LatestMediaView: View { LazyHStack { Spacer().frame(width: 45) ForEach(items, id: \.id) { item in - NavigationLink(destination: LazyView { ItemView(item: item) }) { - PortraitItemElement(item: item) - }.buttonStyle(PlainNavigationLinkButtonStyle()) + if item.type == "Series" || item.type == "Movie" { + NavigationLink(destination: LazyView { ItemView(item: item) }) { + PortraitItemElement(item: item) + }.buttonStyle(PlainNavigationLinkButtonStyle()) + } } Spacer().frame(width: 45) } diff --git a/JellyfinPlayer tvOS/MovieItemView.swift b/JellyfinPlayer tvOS/MovieItemView.swift index e697110d..5cd3567c 100644 --- a/JellyfinPlayer tvOS/MovieItemView.swift +++ b/JellyfinPlayer tvOS/MovieItemView.swift @@ -9,6 +9,7 @@ import SwiftUI import JellyfinAPI +import SwiftUIFocusGuide struct MovieItemView: View { @ObservedObject var viewModel: MovieItemViewModel @@ -17,6 +18,10 @@ struct MovieItemView: View { @State var studio: String? = nil; @State var director: String? = nil; + @State var wrappedScrollView: UIScrollView?; + + @StateObject var focusBag = SwiftUIFocusBag() + @Namespace private var namespace func onAppear() { @@ -45,9 +50,6 @@ struct MovieItemView: View { .opacity(0.4) ScrollView { LazyVStack(alignment: .leading) { - Spacer() //i hate ficus engine - .frame(width: 1920, height: 2) - .focusable() Text(viewModel.item.name ?? "") .font(.title) .fontWeight(.bold) @@ -132,19 +134,14 @@ struct MovieItemView: View { Button { viewModel.updateFavoriteState() } label: { - Image(systemName: "heart.fill") - .foregroundColor(viewModel.isFavorited ? .red : .primary) - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) + MediaViewActionButton(icon: "heart.fill", scrollView: $wrappedScrollView, iconColor: viewModel.isFavorited ? .red : .white) } Text(viewModel.isFavorited ? "Unfavorite" : "Favorite") .font(.caption) } VStack { NavigationLink(destination: VideoPlayerView(item: viewModel.item)) { - Image(systemName: "play.fill") - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) + MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView) } Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : "Play") .font(.caption) @@ -153,19 +150,18 @@ struct MovieItemView: View { Button { viewModel.updateWatchState() } label: { - Image(systemName: "eye.fill") - .foregroundColor(viewModel.isWatched ? .red : .primary) - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) + MediaViewActionButton(icon: "eye.fill", scrollView: $wrappedScrollView, iconColor: viewModel.isWatched ? .red : .white) } Text(viewModel.isWatched ? "Unwatch" : "Mark Watched") .font(.caption) } - }.padding(.top, 15) - Spacer() + Spacer() + } + .padding(.top, 15) + .addFocusGuide(using: focusBag, name: "actionButtons", destinations: [.bottom: "moreLikeThis"], debug: true) } }.padding(.top, 50) - + if(!viewModel.similarItems.isEmpty) { Text("More Like This") .font(.headline) @@ -181,11 +177,21 @@ struct MovieItemView: View { Spacer().frame(width: 45) } }.padding(EdgeInsets(top: -30, leading: -90, bottom: 0, trailing: -90)) + .addFocusGuide(using: focusBag, name: "moreLikeThis", destinations: [.top: "actionButtons"], debug: false) .frame(height: 360) } }.padding(EdgeInsets(top: 90, leading: 90, bottom: 0, trailing: 90)) + }.introspectScrollView { scrollView in + wrappedScrollView = scrollView } }.onAppear(perform: onAppear) .focusScope(namespace) } } + +extension UIScrollView { + func scrollToTop() { + let desiredOffset = CGPoint(x: 0, y: 0) + setContentOffset(desiredOffset, animated: true) + } +} diff --git a/JellyfinPlayer tvOS/SeriesItemView.swift b/JellyfinPlayer tvOS/SeriesItemView.swift index d2341ab3..5234f676 100644 --- a/JellyfinPlayer tvOS/SeriesItemView.swift +++ b/JellyfinPlayer tvOS/SeriesItemView.swift @@ -9,6 +9,7 @@ import SwiftUI import JellyfinAPI +import SwiftUIFocusGuide struct SeriesItemView: View { @ObservedObject var viewModel: SeriesItemViewModel @@ -17,6 +18,10 @@ struct SeriesItemView: View { @State var studio: String? = nil; @State var director: String? = nil; + @State var wrappedScrollView: UIScrollView?; + + @StateObject var focusBag = SwiftUIFocusBag() + @Environment(\.resetFocus) var resetFocus @Namespace private var namespace @@ -45,183 +50,173 @@ struct SeriesItemView: View { ImageView(src: viewModel.item.getBackdropImage(maxWidth: 1920), bh: viewModel.item.getBackdropImageBlurHash()) .opacity(0.4) ScrollView { - ScrollViewReader { reader in - LazyVStack(alignment: .leading) { - Spacer() //i hate ficus engine - .frame(width: 1920, height: 2) - .focusable() - Text(viewModel.item.name ?? "") - .font(.title) - .fontWeight(.bold) - .foregroundColor(.primary) - HStack { - Text(viewModel.getRunYears()).font(.subheadline) - .fontWeight(.medium) + LazyVStack(alignment: .leading) { + Text(viewModel.item.name ?? "") + .font(.title) + .fontWeight(.bold) + .foregroundColor(.primary) + HStack { + Text(viewModel.getRunYears()).font(.subheadline) + .fontWeight(.medium) + .foregroundColor(.secondary) + .lineLimit(1) + if viewModel.item.officialRating != nil { + Text(viewModel.item.officialRating!).font(.subheadline) + .fontWeight(.semibold) .foregroundColor(.secondary) .lineLimit(1) - if viewModel.item.officialRating != nil { - Text(viewModel.item.officialRating!).font(.subheadline) + .padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4)) + .overlay(RoundedRectangle(cornerRadius: 2) + .stroke(Color.secondary, lineWidth: 1)) + } + if viewModel.item.communityRating != nil { + HStack { + Image(systemName: "star.fill") + .foregroundColor(.yellow) + .font(.subheadline) + Text(String(viewModel.item.communityRating!)).font(.subheadline) .fontWeight(.semibold) .foregroundColor(.secondary) .lineLimit(1) - .padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4)) - .overlay(RoundedRectangle(cornerRadius: 2) - .stroke(Color.secondary, lineWidth: 1)) - } - if viewModel.item.communityRating != nil { - HStack { - Image(systemName: "star.fill") - .foregroundColor(.yellow) - .font(.subheadline) - Text(String(viewModel.item.communityRating!)).font(.subheadline) - .fontWeight(.semibold) - .foregroundColor(.secondary) - .lineLimit(1) - } } } - - HStack { - VStack(alignment: .trailing) { - if(studio != nil) { - Text("STUDIO") - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.primary) - Text(studio!) - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.secondary) - .padding(.bottom, 40) - } - - if(director != nil) { - Text("DIRECTOR") - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.primary) - Text(director!) - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.secondary) - .padding(.bottom, 40) - } - - if(!actors.isEmpty) { - Text("CAST") - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.primary) - ForEach(actors, id: \.id) { person in - Text(person.name!) - .font(.body) - .fontWeight(.semibold) - .foregroundColor(.secondary) - } - } - Spacer() - } - VStack(alignment: .leading) { - if(!(viewModel.item.taglines ?? []).isEmpty) { - Text(viewModel.item.taglines?.first ?? "") - .font(.body) - .italic() - .fontWeight(.medium) - .foregroundColor(.primary) - } - Text(viewModel.item.overview ?? "") + } + + HStack { + VStack(alignment: .trailing) { + if(studio != nil) { + Text("STUDIO") .font(.body) + .fontWeight(.semibold) + .foregroundColor(.primary) + Text(studio!) + .font(.body) + .fontWeight(.semibold) + .foregroundColor(.secondary) + .padding(.bottom, 40) + } + + if(director != nil) { + Text("DIRECTOR") + .font(.body) + .fontWeight(.semibold) + .foregroundColor(.primary) + Text(director!) + .font(.body) + .fontWeight(.semibold) + .foregroundColor(.secondary) + .padding(.bottom, 40) + } + + if(!actors.isEmpty) { + Text("CAST") + .font(.body) + .fontWeight(.semibold) + .foregroundColor(.primary) + ForEach(actors, id: \.id) { person in + Text(person.name!) + .font(.body) + .fontWeight(.semibold) + .foregroundColor(.secondary) + } + } + Spacer() + } + VStack(alignment: .leading) { + if(!(viewModel.item.taglines ?? []).isEmpty) { + Text(viewModel.item.taglines?.first ?? "") + .font(.body) + .italic() .fontWeight(.medium) .foregroundColor(.primary) - - HStack { - VStack { - Button { - viewModel.updateFavoriteState() - } label: { - Image(systemName: "heart.fill") - .foregroundColor(viewModel.isFavorited ? .red : .primary) - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) - }.prefersDefaultFocus(in: namespace) - Text(viewModel.isFavorited ? "Unfavorite" : "Favorite") - .font(.caption) - } - if(viewModel.nextUpItem != nil) { - VStack { - NavigationLink(destination: VideoPlayerView(item: viewModel.nextUpItem!)) { - Image(systemName: "play.fill") - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) - } - Text("Play • \(viewModel.nextUpItem!.getEpisodeLocator())") - .font(.caption) - } - } - VStack { - Button { - viewModel.updateWatchState() - } label: { - Image(systemName: "eye.fill") - .foregroundColor(viewModel.isWatched ? .red : .primary) - .font(.system(size: 40)) - .padding(.vertical, 12).padding(.horizontal, 20) - } - Text(viewModel.isWatched ? "Unwatch" : "Mark Watched") - .font(.caption) - } - }.padding(.top, 15) - Spacer() } - }.padding(.top, 50) - - if(viewModel.nextUpItem != nil) { - Text("Next Up") - .font(.headline) - .fontWeight(.semibold) - NavigationLink(destination: ItemView(item: viewModel.nextUpItem!)) { - LandscapeItemElement(item: viewModel.nextUpItem!) - }.buttonStyle(PlainNavigationLinkButtonStyle()).padding(.bottom, 1) - } - - if(!viewModel.seasons.isEmpty) { - Text("Seasons") - .font(.headline) - .fontWeight(.semibold) - ScrollView(.horizontal) { - LazyHStack { - Spacer().frame(width: 45) - ForEach(viewModel.seasons, id: \.id) { season in - NavigationLink(destination: ItemView(item: season)) { - PortraitItemElement(item: season) - }.buttonStyle(PlainNavigationLinkButtonStyle()) - } - Spacer().frame(width: 45) + Text(viewModel.item.overview ?? "") + .font(.body) + .fontWeight(.medium) + .foregroundColor(.primary) + + HStack { + VStack { + Button { + viewModel.updateFavoriteState() + } label: { + MediaViewActionButton(icon: "heart.fill", scrollView: $wrappedScrollView, iconColor: viewModel.isFavorited ? .red : .white) + }.prefersDefaultFocus(in: namespace) + Text(viewModel.isFavorited ? "Unfavorite" : "Favorite") + .font(.caption) } - }.padding(EdgeInsets(top: -30, leading: -90, bottom: 0, trailing: -90)) - .frame(height: 360) - } - - if(!viewModel.similarItems.isEmpty) { - Text("More Like This") - .font(.headline) - .fontWeight(.semibold) - ScrollView(.horizontal) { - LazyHStack { - Spacer().frame(width: 45) - ForEach(viewModel.similarItems, id: \.id) { similarItems in - NavigationLink(destination: ItemView(item: similarItems)) { - PortraitItemElement(item: similarItems) - }.buttonStyle(PlainNavigationLinkButtonStyle()) + if(viewModel.nextUpItem != nil) { + VStack { + NavigationLink(destination: VideoPlayerView(item: viewModel.nextUpItem!)) { + MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView) + } + Text("Play • \(viewModel.nextUpItem!.getEpisodeLocator())") + .font(.caption) } - Spacer().frame(width: 45) } - }.padding(EdgeInsets(top: -30, leading: -90, bottom: 0, trailing: -90)) - .frame(height: 360) + VStack { + Button { + viewModel.updateWatchState() + } label: { + MediaViewActionButton(icon: "eye.fill", scrollView: $wrappedScrollView, iconColor: viewModel.isWatched ? .red : .white) + } + Text(viewModel.isWatched ? "Unwatch" : "Mark Watched") + .font(.caption) + } + }.padding(.top, 15) + Spacer() } - }.padding(EdgeInsets(top: 90, leading: 90, bottom: 45, trailing: 90)) - } + }.padding(.top, 50) + + if(viewModel.nextUpItem != nil) { + Text("Next Up") + .font(.headline) + .fontWeight(.semibold) + NavigationLink(destination: ItemView(item: viewModel.nextUpItem!)) { + LandscapeItemElement(item: viewModel.nextUpItem!) + }.buttonStyle(PlainNavigationLinkButtonStyle()).padding(.bottom, 1) + } + + if(!viewModel.seasons.isEmpty) { + Text("Seasons") + .font(.headline) + .fontWeight(.semibold) + ScrollView(.horizontal) { + LazyHStack { + Spacer().frame(width: 45) + ForEach(viewModel.seasons, id: \.id) { season in + NavigationLink(destination: ItemView(item: season)) { + PortraitItemElement(item: season) + }.buttonStyle(PlainNavigationLinkButtonStyle()) + } + Spacer().frame(width: 45) + } + }.padding(EdgeInsets(top: -30, leading: -90, bottom: 0, trailing: -90)) + .frame(height: 360) + } + + if(!viewModel.similarItems.isEmpty) { + Text("More Like This") + .font(.headline) + .fontWeight(.semibold) + ScrollView(.horizontal) { + LazyHStack { + Spacer().frame(width: 45) + ForEach(viewModel.similarItems, id: \.id) { similarItems in + NavigationLink(destination: ItemView(item: similarItems)) { + PortraitItemElement(item: similarItems) + }.buttonStyle(PlainNavigationLinkButtonStyle()) + } + Spacer().frame(width: 45) + } + }.padding(EdgeInsets(top: -30, leading: -90, bottom: 0, trailing: -90)) + .frame(height: 360) + } + }.padding(EdgeInsets(top: 90, leading: 90, bottom: 45, trailing: 90)) }.focusScope(namespace) + .introspectScrollView { scrollView in + wrappedScrollView = scrollView + } }.onAppear(perform: onAppear) } } diff --git a/JellyfinPlayer tvOS/VideoPlayer/VideoPlayerViewController.swift b/JellyfinPlayer tvOS/VideoPlayer/VideoPlayerViewController.swift index f2b718f5..ccb50005 100644 --- a/JellyfinPlayer tvOS/VideoPlayer/VideoPlayerViewController.swift +++ b/JellyfinPlayer tvOS/VideoPlayer/VideoPlayerViewController.swift @@ -49,16 +49,8 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, var lastTime: Float = 0.0 var startTime: Int = 0 - var selectedAudioTrack: Int32 = -1 { - didSet { - print(selectedAudioTrack) - } - } - var selectedCaptionTrack: Int32 = -1 { - didSet { - print(selectedCaptionTrack) - } - } + var selectedAudioTrack: Int32 = -1 + var selectedCaptionTrack: Int32 = -1 var subtitleTrackArray: [Subtitle] = [] var audioTrackArray: [AudioTrack] = [] @@ -240,22 +232,14 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, if let rawStartTicks = manifest.userData?.playbackPositionTicks { mediaPlayer.jumpForward(Int32(rawStartTicks / 10_000_000)) } - - // Pause and load captions into memory. - mediaPlayer.pause() - + subtitleTrackArray.forEach { sub in if sub.id != -1 && sub.delivery == .external { mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false) } } - // Select default track & resume playback - mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack - mediaPlayer.pause() - mediaPlayer.play() playing = true - setupInfoPanel() }) @@ -305,14 +289,9 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, if let event = remoteEvent as? MPChangePlaybackPositionCommandEvent { let targetSeconds = event.positionTime - print(targetSeconds) - let videoPosition = Double(self.mediaPlayer.time.intValue / 1000) - print(videoPosition) - let offset = targetSeconds - videoPosition - print(offset) - + if offset > 0 { self.mediaPlayer.jumpForward(Int32(offset)) } else { @@ -438,40 +417,44 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, } } - + // MARK: Gestures + override func pressesBegan(_ presses: Set, with event: UIPressesEvent?) { + for item in presses { + if(item.type == .select) { + selectButtonTapped() + } + } + } + func setupGestures() { - + self.becomeFirstResponder() + + //vlc crap + videoContentView.gestureRecognizers?.forEach { gr in + videoContentView.removeGestureRecognizer(gr) + } + videoContentView.subviews.forEach { sv in + sv.gestureRecognizers?.forEach { gr in + sv.removeGestureRecognizer(gr) + } + } + let playPauseGesture = UITapGestureRecognizer(target: self, action: #selector(self.selectButtonTapped)) let playPauseType = UIPress.PressType.playPause playPauseGesture.allowedPressTypes = [NSNumber(value: playPauseType.rawValue)] view.addGestureRecognizer(playPauseGesture) - - let selectGesture = UITapGestureRecognizer(target: self, action: #selector(self.selectButtonTapped)) - let selectType = UIPress.PressType.select - selectGesture.allowedPressTypes = [NSNumber(value: selectType.rawValue)] - view.addGestureRecognizer(selectGesture) - + let backTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.backButtonPressed(tap:))) let backPress = UIPress.PressType.menu backTapGesture.allowedPressTypes = [NSNumber(value: backPress.rawValue)] view.addGestureRecognizer(backTapGesture) - + let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.userPanned(panGestureRecognizer:))) view.addGestureRecognizer(panGestureRecognizer) - - let swipeRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(self.swipe(swipe:))) - swipeRecognizer.direction = .right - view.addGestureRecognizer(swipeRecognizer) - - let swipeRecognizerl = UISwipeGestureRecognizer(target: self, action: #selector(self.swipe(swipe:))) - swipeRecognizerl.direction = .left - view.addGestureRecognizer(swipeRecognizerl) - } @objc func backButtonPressed(tap: UITapGestureRecognizer) { - print("back") // Dismiss info panel if showingInfoPanel { if focusedOnTabBar { @@ -490,6 +473,7 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, seeking = false } else { // Dismiss view + self.resignFirstResponder() mediaPlayer.stop() sendStopReport() self.navigationController?.popViewController(animated: true) @@ -497,16 +481,17 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, } @objc func userPanned(panGestureRecognizer: UIPanGestureRecognizer) { - print("pan") if loading { return } let translation = panGestureRecognizer.translation(in: view) let velocity = panGestureRecognizer.velocity(in: view) + + print(translation) // Swiped up - Handle dismissing info panel - if translation.y < -700 && (focusedOnTabBar && showingInfoPanel) { + if translation.y < -200 && (focusedOnTabBar && showingInfoPanel) { toggleInfoContainer() return } @@ -516,7 +501,7 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, } // Swiped down - Show the info panel - if translation.y > 700 { + if translation.y > 200 { toggleInfoContainer() return } @@ -546,32 +531,9 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate, } - // Not currently used - @objc func swipe(swipe: UISwipeGestureRecognizer!) { - print("swiped") - switch swipe.direction { - case .left: - print("swiped left") -// mediaPlayer.pause() - // player.seek(to: CMTime(value: Int64(self.currentSeconds) + 10, timescale: 1)) -// mediaPlayer.play() - case .right: - print("swiped right") -// mediaPlayer.pause() - // player.seek(to: CMTime(value: Int64(self.currentSeconds) + 10, timescale: 1)) -// mediaPlayer.play() - case .up: - break - case .down: - break - default: - break - } - - } - /// Play/Pause or Select is pressed on the AppleTV remote @objc func selectButtonTapped() { + print("select") if loading { return } diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index 15794b83..dac2d585 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ 5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; }; 5321753E2671DE9C005491E6 /* Typings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535870AC2669D8DD00D05A09 /* Typings.swift */; }; 532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */; }; + 53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53272531268BF09D0035FBF1 /* MediaViewActionButton.swift */; }; + 53272535268BF9710035FBF1 /* SwiftUIFocusGuide in Frameworks */ = {isa = PBXBuildFile; productRef = 53272534268BF9710035FBF1 /* SwiftUIFocusGuide */; }; 532E68CF267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532E68CE267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift */; }; 53313B90265EEA6D00947AA3 /* VideoPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */; }; 53352571265EA0A0006CCA86 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 53352570265EA0A0006CCA86 /* Introspect */; }; @@ -232,6 +234,7 @@ 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = ""; }; 531AC8BE26750DE20091C7EB /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = ""; }; 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; + 53272531268BF09D0035FBF1 /* MediaViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaViewActionButton.swift; sourceTree = ""; }; 532E68CE267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerCastDeviceSelector.swift; sourceTree = ""; }; 53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = VideoPlayer.storyboard; sourceTree = ""; }; 5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = ""; }; @@ -353,6 +356,7 @@ 53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */, 535870912669D7A800D05A09 /* Introspect in Frameworks */, 62CB3F482685BB3B003D0A6F /* Defaults in Frameworks */, + 53272535268BF9710035FBF1 /* SwiftUIFocusGuide in Frameworks */, 5358708D2669D7A800D05A09 /* KeychainSwift in Frameworks */, 536D3D84267BEA550004248C /* ParallaxView in Frameworks */, 53ABFDDC267972BF00886593 /* TVServices.framework in Frameworks */, @@ -503,6 +507,7 @@ 536D3D80267BDFC60004248C /* PortraitItemElement.swift */, 536D3D87267C17350004248C /* PublicUserButton.swift */, 53116A18268B947A003024C9 /* PlainLinkButton.swift */, + 53272531268BF09D0035FBF1 /* MediaViewActionButton.swift */, ); path = Components; sourceTree = ""; @@ -703,6 +708,7 @@ 53ABFDEC26799D7700886593 /* ActivityIndicator */, 536D3D83267BEA550004248C /* ParallaxView */, 62CB3F472685BB3B003D0A6F /* Defaults */, + 53272534268BF9710035FBF1 /* SwiftUIFocusGuide */, ); productName = "JellyfinPlayer tvOS"; productReference = 535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */; @@ -804,6 +810,7 @@ 536D3D82267BEA550004248C /* XCRemoteSwiftPackageReference "ParallaxView" */, 53EC6E23267EB10F006DD26A /* XCRemoteSwiftPackageReference "SwiftyJSON" */, 62CB3F442685BAF7003D0A6F /* XCRemoteSwiftPackageReference "Defaults" */, + 53272533268BF9710035FBF1 /* XCRemoteSwiftPackageReference "SwiftUIFocusGuide" */, ); productRefGroup = 5377CBF2263B596A003A4E83 /* Products */; projectDirPath = ""; @@ -983,6 +990,7 @@ 6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */, 62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */, 535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */, + 53272532268BF09D0035FBF1 /* MediaViewActionButton.swift in Sources */, 531690F0267ABF72005D8AB9 /* NextUpView.swift in Sources */, 535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */, 62E632DD267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */, @@ -1447,6 +1455,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 53272533268BF9710035FBF1 /* XCRemoteSwiftPackageReference "SwiftUIFocusGuide" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/rmnblm/SwiftUIFocusGuide"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.0; + }; + }; 5335256F265EA0A0006CCA86 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/siteline/SwiftUI-Introspect"; @@ -1514,6 +1530,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 53272534268BF9710035FBF1 /* SwiftUIFocusGuide */ = { + isa = XCSwiftPackageProductDependency; + package = 53272533268BF9710035FBF1 /* XCRemoteSwiftPackageReference "SwiftUIFocusGuide" */; + productName = SwiftUIFocusGuide; + }; 53352570265EA0A0006CCA86 /* Introspect */ = { isa = XCSwiftPackageProductDependency; package = 5335256F265EA0A0006CCA86 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; diff --git a/JellyfinPlayer.xcworkspace/xcshareddata/swiftpm/Package.resolved b/JellyfinPlayer.xcworkspace/xcshareddata/swiftpm/Package.resolved index 02abe99e..b208b5a0 100644 --- a/JellyfinPlayer.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/JellyfinPlayer.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -91,6 +91,15 @@ "version": "0.1.3" } }, + { + "package": "SwiftUIFocusGuide", + "repositoryURL": "https://github.com/rmnblm/SwiftUIFocusGuide", + "state": { + "branch": null, + "revision": "fb8eefaccb2954efedc19a5539241f370baa4a10", + "version": "0.1.0" + } + }, { "package": "SwiftyJSON", "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON",