Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle app phases (auto pause/play on enter background/active phase) #831

Merged
merged 1 commit into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
}
4 changes: 4 additions & 0 deletions Shared/Extensions/ViewExtensions/ViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,8 @@ extension View {
.ignoresSafeArea()
}
}

func onScenePhase(_ phase: ScenePhase, _ action: @escaping () -> Void) -> some View {
modifier(ScenePhaseChangeModifier(phase: phase, action: action))
}
}
5 changes: 5 additions & 0 deletions Shared/Services/SwiftfinDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions Swiftfin tvOS/Views/SettingsView/VideoPlayerSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions Swiftfin tvOS/Views/VideoPlayer/NativeVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import SwiftUI

struct NativeVideoPlayer: View {

@Environment(\.scenePhase)
var scenePhase

@EnvironmentObject
private var router: VideoPlayerCoordinator.Router

Expand Down
13 changes: 13 additions & 0 deletions Swiftfin tvOS/Views/VideoPlayer/VideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ struct VideoPlayer: View {
case smallMenu
}

@Environment(\.scenePhase)
private var scenePhase

@EnvironmentObject
private var router: VideoPlayerCoordinator.Router

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

Expand Down
6 changes: 6 additions & 0 deletions Swiftfin.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -2244,6 +2247,7 @@
E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */,
E129428428F080B500796AC6 /* OnReceiveNotificationModifier.swift */,
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */,
E43918652AD5C8310045A18C /* ScenePhaseChangeModifier.swift */,
);
path = Modifiers;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions Swiftfin/Views/VideoPlayer/NativeVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import SwiftUI

struct NativeVideoPlayer: View {

@Environment(\.scenePhase)
var scenePhase

@EnvironmentObject
private var router: VideoPlayerCoordinator.Router

Expand Down
13 changes: 13 additions & 0 deletions Swiftfin/Views/VideoPlayer/VideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ struct VideoPlayer: View {
case chapters
}

@Environment(\.scenePhase)
private var scenePhase

class GestureStateHandler {

var beganPanWithOverlay: Bool = false
Expand Down Expand Up @@ -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()
}
}
}
}

Expand Down