confirm close tfor tvos

This commit is contained in:
Ethan Pippin 2022-01-03 23:37:48 -07:00
parent 2d7cad8cec
commit 25b4e382f2
6 changed files with 154 additions and 25 deletions

View File

@ -19,6 +19,7 @@ struct SettingsView: View {
@Default(.videoPlayerJumpForward) var jumpForwardLength
@Default(.videoPlayerJumpBackward) var jumpBackwardLength
@Default(.downActionShowsMenu) var downActionShowsMenu
@Default(.confirmClose) var confirmClose
var body: some View {
GeometryReader { reader in
@ -76,6 +77,8 @@ struct SettingsView: View {
Toggle("Press Down for Menu", isOn: $downActionShowsMenu)
Toggle("Confirm Close", isOn: $confirmClose)
Button {
settingsRouter.route(to: \.overlaySettings)
} label: {

View File

@ -29,6 +29,7 @@ class VLCPlayerViewController: UIViewController {
private var lastProgressReportTicks: Int64 = 0
private var viewModelListeners = Set<AnyCancellable>()
private var overlayDismissTimer: Timer?
private var confirmCloseOverlayDismissTimer: Timer?
private var currentPlayerTicks: Int64 {
return Int64(vlcMediaPlayer.time.intValue) * 100_000
@ -42,11 +43,16 @@ class VLCPlayerViewController: UIViewController {
return currentOverlayContentHostingController?.view.alpha ?? 0 > 0
}
private var displayingConfirmClose: Bool {
return currentConfirmCloseHostingController?.view.alpha ?? 0 > 0
}
private lazy var videoContentView = makeVideoContentView()
private lazy var jumpBackwardOverlayView = makeJumpBackwardOverlayView()
private lazy var jumpForwardOverlayView = makeJumpForwardOverlayView()
private var currentOverlayHostingController: UIHostingController<tvOSVLCOverlay>?
private var currentOverlayContentHostingController: UIHostingController<SmallMediaStreamSelectionView>?
private var currentConfirmCloseHostingController: UIHostingController<ConfirmCloseOverlay>?
// MARK: init
@ -192,10 +198,16 @@ class VLCPlayerViewController: UIViewController {
switch(buttonPress) {
case .menu: () // Captured by other gesture
case .playPause:
hideConfirmCloseOverlay()
didSelectMain()
case .select:
hideConfirmCloseOverlay()
didGenerallyTap()
case .upArrow:
hideConfirmCloseOverlay()
if displayingContentOverlay {
hideOverlayContent()
@ -203,16 +215,22 @@ class VLCPlayerViewController: UIViewController {
restartOverlayDismissTimer()
}
case .downArrow:
hideConfirmCloseOverlay()
if Defaults[.downActionShowsMenu] {
if !displayingContentOverlay {
didSelectMenu()
}
}
case .leftArrow:
hideConfirmCloseOverlay()
if !displayingContentOverlay {
didSelectBackward()
}
case .rightArrow:
hideConfirmCloseOverlay()
if !displayingContentOverlay {
didSelectForward()
}
@ -229,6 +247,7 @@ class VLCPlayerViewController: UIViewController {
view.addGestureRecognizer(pressRecognizer)
}
// MARK: didPressMenu
@objc private func didPressMenu() {
if displayingOverlay {
hideOverlay()
@ -237,6 +256,11 @@ class VLCPlayerViewController: UIViewController {
showOverlay()
restartOverlayDismissTimer()
} else if viewModel.confirmClose && !displayingConfirmClose {
showConfirmCloseOverlay()
restartConfirmCloseDismissTimer()
} else {
vlcMediaPlayer.pause()
@ -255,7 +279,7 @@ class VLCPlayerViewController: UIViewController {
// TODO: Look at injecting viewModel into the environment so it updates the current overlay
// Overlay
// Main overlay
if let currentOverlayHostingController = currentOverlayHostingController {
// UX fade-out
UIView.animate(withDuration: 0.5) {
@ -295,7 +319,7 @@ class VLCPlayerViewController: UIViewController {
self.currentOverlayHostingController = newOverlayHostingController
// OverlayContent
// Media Stream selection
if let currentOverlayContentHostingController = currentOverlayContentHostingController {
currentOverlayContentHostingController.view.isHidden = true
@ -303,26 +327,9 @@ class VLCPlayerViewController: UIViewController {
currentOverlayContentHostingController.removeFromParent()
}
// let newSmallMenuOverlayView = SmallMediaStreamSelectionView(viewModel: viewModel,
// title: "Subtitles",
// items: viewModel.subtitleStreams) { selectedMediaStream in
// self.didSelectSubtitleStream(index: selectedMediaStream.index ?? -1)
// }
// let newSmallMenuOverlayView = SmallMediaStreamSelectionView(viewModel: viewModel,
// items: viewModel.subtitleStreams,
// selectedIndex: viewModel.selectedSubtitleStreamIndex,
// title: "Subtitles") { selectedMediaStream in
// DispatchQueue.main.async {
// self.viewModel.selectedSubtitleStreamIndex = selectedMediaStream.index ?? -1
// self.didSelectSubtitleStream(index: selectedMediaStream.index ?? -1)
// }
// }
let newSmallMenuOverlayView = SmallMediaStreamSelectionView(viewModel: viewModel)
let newOverlayContentHostingController = UIHostingController(rootView: newSmallMenuOverlayView)
// let newOverlayContentView = tvOSOverlayContentView(viewModel: viewModel)
// let newOverlayContentHostingController = UIHostingController(rootView: newOverlayContentView)
newOverlayContentHostingController.view.translatesAutoresizingMaskIntoConstraints = false
newOverlayContentHostingController.view.backgroundColor = UIColor.clear
@ -341,6 +348,36 @@ class VLCPlayerViewController: UIViewController {
])
self.currentOverlayContentHostingController = newOverlayContentHostingController
// Confirm close
if let currentConfirmCloseHostingController = currentConfirmCloseHostingController {
currentConfirmCloseHostingController.view.isHidden = true
currentConfirmCloseHostingController.view.removeFromSuperview()
currentConfirmCloseHostingController.removeFromParent()
}
let newConfirmCloseOverlay = ConfirmCloseOverlay()
let newConfirmCloseHostingController = UIHostingController(rootView: newConfirmCloseOverlay)
newConfirmCloseHostingController.view.translatesAutoresizingMaskIntoConstraints = false
newConfirmCloseHostingController.view.backgroundColor = UIColor.clear
newConfirmCloseHostingController.view.alpha = 0
addChild(newConfirmCloseHostingController)
view.addSubview(newConfirmCloseHostingController.view)
newConfirmCloseHostingController.didMove(toParent: self)
NSLayoutConstraint.activate([
newConfirmCloseHostingController.view.topAnchor.constraint(equalTo: videoContentView.topAnchor),
newConfirmCloseHostingController.view.bottomAnchor.constraint(equalTo: videoContentView.bottomAnchor),
newConfirmCloseHostingController.view.leftAnchor.constraint(equalTo: videoContentView.leftAnchor),
newConfirmCloseHostingController.view.rightAnchor.constraint(equalTo: videoContentView.rightAnchor)
])
self.currentConfirmCloseHostingController = newConfirmCloseHostingController
// There is a behavior when setting this that the navigation bar
// on the current navigation controller pops up, re-hide it
@ -543,6 +580,26 @@ extension VLCPlayerViewController {
}
}
// MARK: Show/Hide Confirm close
extension VLCPlayerViewController {
private func showConfirmCloseOverlay() {
guard let currentConfirmCloseHostingController = currentConfirmCloseHostingController else { return }
UIView.animate(withDuration: 0.2) {
currentConfirmCloseHostingController.view.alpha = 1
}
}
private func hideConfirmCloseOverlay() {
guard let currentConfirmCloseHostingController = currentConfirmCloseHostingController else { return }
UIView.animate(withDuration: 0.5) {
currentConfirmCloseHostingController.view.alpha = 0
}
}
}
// MARK: OverlayTimer
extension VLCPlayerViewController {
@ -552,11 +609,28 @@ extension VLCPlayerViewController {
}
@objc private func dismissTimerFired() {
self.hideOverlay()
hideOverlay()
}
private func stopOverlayDismissTimer() {
self.overlayDismissTimer?.invalidate()
overlayDismissTimer?.invalidate()
}
}
// MARK: Confirm Close Overlay Timer
extension VLCPlayerViewController {
private func restartConfirmCloseDismissTimer() {
self.confirmCloseOverlayDismissTimer?.invalidate()
self.confirmCloseOverlayDismissTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(confirmCloseTimerFired), userInfo: nil, repeats: false)
}
@objc private func confirmCloseTimerFired() {
hideConfirmCloseOverlay()
}
private func stopConfirmCloseDismissTimer() {
confirmCloseOverlayDismissTimer?.invalidate()
}
}

View File

@ -0,0 +1,40 @@
//
/*
* 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 ConfirmCloseOverlay: View {
var body: some View {
VStack {
HStack {
Image(systemName: "chevron.left.circle.fill")
.font(.system(size: 96))
.padding(3)
.background(Color.black.opacity(0.4).mask(Circle()))
Spacer()
}
.padding()
Spacer()
}
.padding()
}
}
struct ConfirmCloseOverlay_Previews: PreviewProvider {
static var previews: some View {
ZStack {
Color.red.ignoresSafeArea()
ConfirmCloseOverlay()
.ignoresSafeArea()
}
}
}

View File

@ -385,6 +385,7 @@
E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */; };
E1E5D54F2783E67100692DFE /* OverlaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D54E2783E67100692DFE /* OverlaySettingsView.swift */; };
E1E5D5512783E67700692DFE /* ExperimentalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5502783E67700692DFE /* ExperimentalSettingsView.swift */; };
E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D552278419D900692DFE /* ConfirmCloseOverlay.swift */; };
E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; };
E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; };
E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */; };
@ -670,6 +671,7 @@
E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentalSettingsView.swift; sourceTree = "<group>"; };
E1E5D54E2783E67100692DFE /* OverlaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlaySettingsView.swift; sourceTree = "<group>"; };
E1E5D5502783E67700692DFE /* ExperimentalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentalSettingsView.swift; sourceTree = "<group>"; };
E1E5D552278419D900692DFE /* ConfirmCloseOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmCloseOverlay.swift; sourceTree = "<group>"; };
E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerJumpLength.swift; sourceTree = "<group>"; };
E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallMenuOverlay.swift; sourceTree = "<group>"; };
E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
@ -1332,6 +1334,7 @@
E17885A7278130690094FBCF /* tvOSOverlay */ = {
isa = PBXGroup;
children = (
E1E5D552278419D900692DFE /* ConfirmCloseOverlay.swift */,
E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */,
E178859F2780F55C0094FBCF /* tvOSVLCOverlay.swift */,
);
@ -1983,6 +1986,7 @@
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
53649AB2269D019100A2D8B7 /* LogManager.swift in Sources */,
E1E5D553278419D900692DFE /* ConfirmCloseOverlay.swift in Sources */,
E13DD3D6271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
E193D549271941CC00900D82 /* UserSignInView.swift in Sources */,
535870AA2669D8AE00D05A09 /* BlurHashDecode.swift in Sources */,

View File

@ -59,4 +59,5 @@ extension Defaults.Keys {
// tvos specific
static let downActionShowsMenu = Key<Bool>("downActionShowsMenu", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let confirmClose = Key<Bool>("confirmClose", default: false, suite: SwiftfinStore.Defaults.generalSuite)
}

View File

@ -92,6 +92,9 @@ final class VideoPlayerViewModel: ViewModel {
// MARK: Experimental
let syncSubtitleStateWithAdjacent: Bool
// MARK: tvOS
let confirmClose: Bool
// Full response kept for convenience
let response: PlaybackInfoResponse
@ -161,6 +164,8 @@ final class VideoPlayerViewModel: ViewModel {
self.syncSubtitleStateWithAdjacent = Defaults[.Experimental.syncSubtitleStateWithAdjacent]
self.confirmClose = Defaults[.confirmClose]
super.init()
self.sliderPercentage = (item.userData?.playedPercentage ?? 0) / 100
@ -355,7 +360,7 @@ extension VideoPlayerViewModel {
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { _ in
print("Playback start report sent!")
LogManager.shared.log.debug("Start report sent for item: \(self.item.id ?? "No ID")")
}
.store(in: &cancellables)
}
@ -391,7 +396,7 @@ extension VideoPlayerViewModel {
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { _ in
print("Pause report sent!")
LogManager.shared.log.debug("Pause report sent for item: \(self.item.id ?? "No ID")")
}
.store(in: &cancellables)
}
@ -434,9 +439,11 @@ extension VideoPlayerViewModel {
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { _ in
print("Playback progress sent!")
LogManager.shared.log.debug("Playback progress sent for item: \(self.item.id ?? "No ID")")
}
.store(in: &cancellables)
self.lastProgressReport = nil
}
// MARK: sendStopReport
@ -458,7 +465,7 @@ extension VideoPlayerViewModel {
.sink { completion in
self.handleAPIRequestError(completion: completion)
} receiveValue: { _ in
print("Playback stop report sent!")
LogManager.shared.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
}
.store(in: &cancellables)
}