Handle app phases (auto pause/play on enter background/active phase) (#831)
There's some leftover stuff like adding the `scenePhase` environment variable but I'll get that sometime.
This commit is contained in:
parent
69ddc2f628
commit
667d48b0e9
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// 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 (c) 2023 Jellyfin & Jellyfin Contributors
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ScenePhaseChangeModifier: ViewModifier {
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
private var scenePhase
|
||||||
|
|
||||||
|
let phase: ScenePhase
|
||||||
|
let action: () -> Void
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content.onChange(of: scenePhase) { newValue in
|
||||||
|
if newValue == phase {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -218,4 +218,8 @@ extension View {
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onScenePhase(_ phase: ScenePhase, _ action: @escaping () -> Void) -> some View {
|
||||||
|
modifier(ScenePhaseChangeModifier(phase: phase, action: action))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,11 @@ extension Defaults.Keys {
|
||||||
)
|
)
|
||||||
static let subtitleSize: Key<Int> = .init("subtitleSize", default: 16, suite: .generalSuite)
|
static let subtitleSize: Key<Int> = .init("subtitleSize", default: 16, suite: .generalSuite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Transition {
|
||||||
|
static let pauseOnBackground: Key<Bool> = .init("pauseOnBackground", default: false, suite: .generalSuite)
|
||||||
|
static let playOnActive: Key<Bool> = .init("playOnActive", default: false, suite: .generalSuite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental settings
|
// Experimental settings
|
||||||
|
|
|
@ -21,6 +21,11 @@ struct VideoPlayerSettingsView: View {
|
||||||
@Default(.VideoPlayer.resumeOffset)
|
@Default(.VideoPlayer.resumeOffset)
|
||||||
private var resumeOffset
|
private var resumeOffset
|
||||||
|
|
||||||
|
@Default(.VideoPlayer.Transition.pauseOnBackground)
|
||||||
|
private var pauseOnBackground
|
||||||
|
@Default(.VideoPlayer.Transition.playOnActive)
|
||||||
|
private var playOnActive
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: VideoPlayerSettingsCoordinator.Router
|
private var router: VideoPlayerSettingsCoordinator.Router
|
||||||
|
|
||||||
|
@ -57,6 +62,12 @@ struct VideoPlayerSettingsView: View {
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Settings only affect some subtitle types")
|
Text("Settings only affect some subtitle types")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
|
||||||
|
Toggle("Pause on background", isOn: $pauseOnBackground)
|
||||||
|
Toggle("Play on active", isOn: $playOnActive)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Video Player")
|
.navigationTitle("Video Player")
|
||||||
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {
|
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {
|
||||||
|
|
|
@ -14,6 +14,9 @@ import SwiftUI
|
||||||
|
|
||||||
struct NativeVideoPlayer: View {
|
struct NativeVideoPlayer: View {
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
var scenePhase
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: VideoPlayerCoordinator.Router
|
private var router: VideoPlayerCoordinator.Router
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ struct VideoPlayer: View {
|
||||||
case smallMenu
|
case smallMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
private var scenePhase
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: VideoPlayerCoordinator.Router
|
private var router: VideoPlayerCoordinator.Router
|
||||||
|
|
||||||
|
@ -100,6 +103,16 @@ struct VideoPlayer: View {
|
||||||
guard !newValue else { return }
|
guard !newValue else { return }
|
||||||
videoPlayerManager.proxy.setTime(.seconds(currentProgressHandler.scrubbedSeconds))
|
videoPlayerManager.proxy.setTime(.seconds(currentProgressHandler.scrubbedSeconds))
|
||||||
}
|
}
|
||||||
|
.onScenePhase(.active) {
|
||||||
|
if Defaults[.VideoPlayer.Transition.playOnActive] {
|
||||||
|
videoPlayerManager.proxy.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onScenePhase(.background) {
|
||||||
|
if Defaults[.VideoPlayer.Transition.pauseOnBackground] {
|
||||||
|
videoPlayerManager.proxy.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -727,6 +727,8 @@
|
||||||
E1FE69A728C29B720021BC93 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A628C29B720021BC93 /* ProgressBar.swift */; };
|
E1FE69A728C29B720021BC93 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A628C29B720021BC93 /* ProgressBar.swift */; };
|
||||||
E1FE69A828C29B720021BC93 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A628C29B720021BC93 /* ProgressBar.swift */; };
|
E1FE69A828C29B720021BC93 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A628C29B720021BC93 /* ProgressBar.swift */; };
|
||||||
E1FE69AA28C29CC20021BC93 /* LandscapePosterProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A928C29CC20021BC93 /* LandscapePosterProgressBar.swift */; };
|
E1FE69AA28C29CC20021BC93 /* LandscapePosterProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FE69A928C29CC20021BC93 /* LandscapePosterProgressBar.swift */; };
|
||||||
|
E43918662AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */; };
|
||||||
|
E43918672AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
@ -1277,6 +1279,7 @@
|
||||||
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = "<group>"; };
|
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = "<group>"; };
|
||||||
E1FE69A628C29B720021BC93 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
|
E1FE69A628C29B720021BC93 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
|
||||||
E1FE69A928C29CC20021BC93 /* LandscapePosterProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandscapePosterProgressBar.swift; sourceTree = "<group>"; };
|
E1FE69A928C29CC20021BC93 /* LandscapePosterProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandscapePosterProgressBar.swift; sourceTree = "<group>"; };
|
||||||
|
E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScenePhaseChangeModifier.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -2244,6 +2247,7 @@
|
||||||
E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */,
|
E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */,
|
||||||
E129428428F080B500796AC6 /* OnReceiveNotificationModifier.swift */,
|
E129428428F080B500796AC6 /* OnReceiveNotificationModifier.swift */,
|
||||||
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */,
|
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */,
|
||||||
|
E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */,
|
||||||
);
|
);
|
||||||
path = Modifiers;
|
path = Modifiers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -3173,6 +3177,7 @@
|
||||||
E193D53927193F8E00900D82 /* SearchCoordinator.swift in Sources */,
|
E193D53927193F8E00900D82 /* SearchCoordinator.swift in Sources */,
|
||||||
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */,
|
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */,
|
||||||
E148128928C154BF003B8787 /* ItemFilter.swift in Sources */,
|
E148128928C154BF003B8787 /* ItemFilter.swift in Sources */,
|
||||||
|
E43918672AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */,
|
||||||
E154966F296CA2EF00C4EF88 /* LogManager.swift in Sources */,
|
E154966F296CA2EF00C4EF88 /* LogManager.swift in Sources */,
|
||||||
E1575E75293E77B5001665B1 /* LibraryViewType.swift in Sources */,
|
E1575E75293E77B5001665B1 /* LibraryViewType.swift in Sources */,
|
||||||
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
|
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
|
||||||
|
@ -3513,6 +3518,7 @@
|
||||||
E1C8CE7C28FF015000DF5D7B /* TrailingTimestampType.swift in Sources */,
|
E1C8CE7C28FF015000DF5D7B /* TrailingTimestampType.swift in Sources */,
|
||||||
E1FE69A728C29B720021BC93 /* ProgressBar.swift in Sources */,
|
E1FE69A728C29B720021BC93 /* ProgressBar.swift in Sources */,
|
||||||
E13332912953B91000EE76AB /* DownloadTaskCoordinator.swift in Sources */,
|
E13332912953B91000EE76AB /* DownloadTaskCoordinator.swift in Sources */,
|
||||||
|
E43918662AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */,
|
||||||
E1B33ED128EB860A0073B0FD /* LargePlaybackButtons.swift in Sources */,
|
E1B33ED128EB860A0073B0FD /* LargePlaybackButtons.swift in Sources */,
|
||||||
E1549664296CA2EF00C4EF88 /* SwiftfinStore.swift in Sources */,
|
E1549664296CA2EF00C4EF88 /* SwiftfinStore.swift in Sources */,
|
||||||
E113133228BDC72000930F75 /* FilterView.swift in Sources */,
|
E113133228BDC72000930F75 /* FilterView.swift in Sources */,
|
||||||
|
|
|
@ -54,6 +54,11 @@ struct VideoPlayerSettingsView: View {
|
||||||
@Default(.VideoPlayer.Overlay.timestampType)
|
@Default(.VideoPlayer.Overlay.timestampType)
|
||||||
private var timestampType
|
private var timestampType
|
||||||
|
|
||||||
|
@Default(.VideoPlayer.Transition.pauseOnBackground)
|
||||||
|
private var pauseOnBackground
|
||||||
|
@Default(.VideoPlayer.Transition.playOnActive)
|
||||||
|
private var playOnActive
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: VideoPlayerSettingsCoordinator.Router
|
private var router: VideoPlayerSettingsCoordinator.Router
|
||||||
|
|
||||||
|
@ -149,6 +154,12 @@ struct VideoPlayerSettingsView: View {
|
||||||
|
|
||||||
EnumPicker(title: "Trailing Value", selection: $trailingTimestampType)
|
EnumPicker(title: "Trailing Value", selection: $trailingTimestampType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section("Transition") {
|
||||||
|
|
||||||
|
Toggle("Pause on background", isOn: $pauseOnBackground)
|
||||||
|
Toggle("Play on active", isOn: $playOnActive)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Video Player")
|
.navigationTitle("Video Player")
|
||||||
.onChange(of: barActionButtons) { newValue in
|
.onChange(of: barActionButtons) { newValue in
|
||||||
|
|
|
@ -14,6 +14,9 @@ import SwiftUI
|
||||||
|
|
||||||
struct NativeVideoPlayer: View {
|
struct NativeVideoPlayer: View {
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
var scenePhase
|
||||||
|
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: VideoPlayerCoordinator.Router
|
private var router: VideoPlayerCoordinator.Router
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ struct VideoPlayer: View {
|
||||||
case chapters
|
case chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
private var scenePhase
|
||||||
|
|
||||||
class GestureStateHandler {
|
class GestureStateHandler {
|
||||||
|
|
||||||
var beganPanWithOverlay: Bool = false
|
var beganPanWithOverlay: Bool = false
|
||||||
|
@ -241,6 +244,16 @@ struct VideoPlayer: View {
|
||||||
audioOffset = 0
|
audioOffset = 0
|
||||||
subtitleOffset = 0
|
subtitleOffset = 0
|
||||||
}
|
}
|
||||||
|
.onScenePhase(.active) {
|
||||||
|
if Defaults[.VideoPlayer.Transition.playOnActive] {
|
||||||
|
videoPlayerManager.proxy.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onScenePhase(.background) {
|
||||||
|
if Defaults[.VideoPlayer.Transition.pauseOnBackground] {
|
||||||
|
videoPlayerManager.proxy.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue