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:
Sylvain CECCHETTO 2023-10-11 04:54:47 +02:00 committed by GitHub
parent 69ddc2f628
commit 667d48b0e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 0 deletions

View File

@ -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()
}
}
}
}

View File

@ -218,4 +218,8 @@ extension View {
.ignoresSafeArea()
}
}
func onScenePhase(_ phase: ScenePhase, _ action: @escaping () -> Void) -> some View {
modifier(ScenePhaseChangeModifier(phase: phase, action: action))
}
}

View File

@ -194,6 +194,11 @@ extension Defaults.Keys {
)
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

View File

@ -21,6 +21,11 @@ struct VideoPlayerSettingsView: View {
@Default(.VideoPlayer.resumeOffset)
private var resumeOffset
@Default(.VideoPlayer.Transition.pauseOnBackground)
private var pauseOnBackground
@Default(.VideoPlayer.Transition.playOnActive)
private var playOnActive
@EnvironmentObject
private var router: VideoPlayerSettingsCoordinator.Router
@ -57,6 +62,12 @@ struct VideoPlayerSettingsView: View {
} footer: {
Text("Settings only affect some subtitle types")
}
Section {
Toggle("Pause on background", isOn: $pauseOnBackground)
Toggle("Play on active", isOn: $playOnActive)
}
}
.navigationTitle("Video Player")
.blurFullScreenCover(isPresented: $isPresentingResumeOffsetStepper) {

View File

@ -14,6 +14,9 @@ import SwiftUI
struct NativeVideoPlayer: View {
@Environment(\.scenePhase)
var scenePhase
@EnvironmentObject
private var router: VideoPlayerCoordinator.Router

View File

@ -19,6 +19,9 @@ struct VideoPlayer: View {
case smallMenu
}
@Environment(\.scenePhase)
private var scenePhase
@EnvironmentObject
private var router: VideoPlayerCoordinator.Router
@ -100,6 +103,16 @@ struct VideoPlayer: View {
guard !newValue else { return }
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()
}
}
}
}

View File

@ -727,6 +727,8 @@
E1FE69A728C29B720021BC93 /* 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 */; };
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 */
/* Begin PBXCopyFilesBuildPhase section */
@ -1277,6 +1279,7 @@
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@ -2244,6 +2247,7 @@
E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */,
E129428428F080B500796AC6 /* OnReceiveNotificationModifier.swift */,
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */,
E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */,
);
path = Modifiers;
sourceTree = "<group>";
@ -3173,6 +3177,7 @@
E193D53927193F8E00900D82 /* SearchCoordinator.swift in Sources */,
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */,
E148128928C154BF003B8787 /* ItemFilter.swift in Sources */,
E43918672AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */,
E154966F296CA2EF00C4EF88 /* LogManager.swift in Sources */,
E1575E75293E77B5001665B1 /* LibraryViewType.swift in Sources */,
E193D53427193F7F00900D82 /* HomeCoordinator.swift in Sources */,
@ -3513,6 +3518,7 @@
E1C8CE7C28FF015000DF5D7B /* TrailingTimestampType.swift in Sources */,
E1FE69A728C29B720021BC93 /* ProgressBar.swift in Sources */,
E13332912953B91000EE76AB /* DownloadTaskCoordinator.swift in Sources */,
E43918662AD5C8310045A18C /* ScenePhaseChangeModifier.swift in Sources */,
E1B33ED128EB860A0073B0FD /* LargePlaybackButtons.swift in Sources */,
E1549664296CA2EF00C4EF88 /* SwiftfinStore.swift in Sources */,
E113133228BDC72000930F75 /* FilterView.swift in Sources */,

View File

@ -54,6 +54,11 @@ struct VideoPlayerSettingsView: View {
@Default(.VideoPlayer.Overlay.timestampType)
private var timestampType
@Default(.VideoPlayer.Transition.pauseOnBackground)
private var pauseOnBackground
@Default(.VideoPlayer.Transition.playOnActive)
private var playOnActive
@EnvironmentObject
private var router: VideoPlayerSettingsCoordinator.Router
@ -149,6 +154,12 @@ struct VideoPlayerSettingsView: View {
EnumPicker(title: "Trailing Value", selection: $trailingTimestampType)
}
Section("Transition") {
Toggle("Pause on background", isOn: $pauseOnBackground)
Toggle("Play on active", isOn: $playOnActive)
}
}
.navigationTitle("Video Player")
.onChange(of: barActionButtons) { newValue in

View File

@ -14,6 +14,9 @@ import SwiftUI
struct NativeVideoPlayer: View {
@Environment(\.scenePhase)
var scenePhase
@EnvironmentObject
private var router: VideoPlayerCoordinator.Router

View File

@ -24,6 +24,9 @@ struct VideoPlayer: View {
case chapters
}
@Environment(\.scenePhase)
private var scenePhase
class GestureStateHandler {
var beganPanWithOverlay: Bool = false
@ -241,6 +244,16 @@ struct VideoPlayer: View {
audioOffset = 0
subtitleOffset = 0
}
.onScenePhase(.active) {
if Defaults[.VideoPlayer.Transition.playOnActive] {
videoPlayerManager.proxy.play()
}
}
.onScenePhase(.background) {
if Defaults[.VideoPlayer.Transition.pauseOnBackground] {
videoPlayerManager.proxy.pause()
}
}
}
}