mirror of
https://github.com/yattee/yattee.git
synced 2024-12-13 05:40:32 +05:30
simplified fullscreen and orientation handling
- iPad: rotate to device orientation on startup - fixed controls in portrait fullscreen - iOS: don’t call setNeedsDrawing multiple times - On iOS we call set needs drawing only once. - Added cooldown time to MPV.Client setNeedsDrawing to avoid multiple successive calls - make fullscreen animation smoother - dragGesture now calls toggleFullScreenAction - fix tvOS and macOS build Signed-off-by: Toni Förster <toni.foerster@gmail.com>
This commit is contained in:
parent
9bf3df1a29
commit
322a550666
@ -44,7 +44,7 @@ final class PlayerSettingsGroupExporter: SettingsGroupExporter {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
export["honorSystemOrientationLock"].bool = Defaults[.honorSystemOrientationLock]
|
export["isOrientationLocked"].bool = Defaults[.isOrientationLocked]
|
||||||
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
|
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
|
||||||
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
|
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
|
||||||
#endif
|
#endif
|
||||||
|
@ -97,8 +97,8 @@ struct PlayerSettingsGroupImporter {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if let honorSystemOrientationLock = json["honorSystemOrientationLock"].bool {
|
if let isOrientationLocked = json["isOrientationLocked"].bool {
|
||||||
Defaults[.honorSystemOrientationLock] = honorSystemOrientationLock
|
Defaults[.isOrientationLocked] = isOrientationLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {
|
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {
|
||||||
|
@ -14,6 +14,8 @@ final class MPVClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var logger = Logger(label: "mpv-client")
|
private var logger = Logger(label: "mpv-client")
|
||||||
|
private var needsDrawingCooldown = false
|
||||||
|
private var needsDrawingWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
var mpv: OpaquePointer!
|
var mpv: OpaquePointer!
|
||||||
var mpvGL: OpaquePointer!
|
var mpvGL: OpaquePointer!
|
||||||
@ -389,10 +391,30 @@ final class MPVClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setNeedsDrawing(_ needsDrawing: Bool) {
|
func setNeedsDrawing(_ needsDrawing: Bool) {
|
||||||
|
// Check if we are currently in a cooldown period
|
||||||
|
guard !needsDrawingCooldown else {
|
||||||
|
logger.info("Not drawing, cooldown in progress")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("needs drawing: \(needsDrawing)")
|
logger.info("needs drawing: \(needsDrawing)")
|
||||||
|
|
||||||
|
// Set the cooldown flag to true and cancel any existing work item
|
||||||
|
needsDrawingCooldown = true
|
||||||
|
needsDrawingWorkItem?.cancel()
|
||||||
|
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
glView?.needsDrawing = needsDrawing
|
glView?.needsDrawing = needsDrawing
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Create a new DispatchWorkItem to reset the cooldown flag after 0.1 seconds
|
||||||
|
let workItem = DispatchWorkItem { [weak self] in
|
||||||
|
self?.needsDrawingCooldown = false
|
||||||
|
}
|
||||||
|
needsDrawingWorkItem = workItem
|
||||||
|
|
||||||
|
// Schedule the cooldown reset after 0.1 seconds
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: workItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func command(
|
func command(
|
||||||
|
@ -56,7 +56,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
||||||
@Published var activeBackend = PlayerBackendType.mpv
|
@Published var activeBackend = PlayerBackendType.mpv
|
||||||
@Published var forceBackendOnPlay: PlayerBackendType?
|
@Published var forceBackendOnPlay: PlayerBackendType?
|
||||||
@Published var wasFullscreen = false
|
|
||||||
|
|
||||||
var avPlayerBackend = AVPlayerBackend()
|
var avPlayerBackend = AVPlayerBackend()
|
||||||
var mpvBackend = MPVBackend()
|
var mpvBackend = MPVBackend()
|
||||||
@ -131,6 +130,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
@Published var lockedOrientation: UIInterfaceOrientationMask?
|
||||||
|
@Published var isOrientationLocked: Bool {
|
||||||
|
didSet {
|
||||||
|
Defaults[.isOrientationLocked] = isOrientationLocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -201,6 +206,16 @@ final class PlayerModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
#if os(iOS)
|
||||||
|
isOrientationLocked = Defaults[.isOrientationLocked]
|
||||||
|
|
||||||
|
if isOrientationLocked, Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
lockedOrientation = UIInterfaceOrientationMask.portrait
|
||||||
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else if isOrientationLocked {
|
||||||
|
lockOrientationAction()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
mpvBackend.controller = mpvController
|
mpvBackend.controller = mpvController
|
||||||
mpvBackend.client = mpvController.client
|
mpvBackend.client = mpvController.client
|
||||||
@ -517,7 +532,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handlePresentationChange() {
|
private func handlePresentationChange() {
|
||||||
|
#if !os(iOS)
|
||||||
|
// TODO: Check whether this is neede on tvOS and macOS
|
||||||
backend.setNeedsDrawing(presentingPlayer)
|
backend.setNeedsDrawing(presentingPlayer)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if presentingPlayer, activeBackend == .appleAVPlayer, avPlayerUsesSystemControls, Constants.isIPhone {
|
if presentingPlayer, activeBackend == .appleAVPlayer, avPlayerUsesSystemControls, Constants.isIPhone {
|
||||||
@ -551,8 +569,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
|
|
||||||
OrientationModel.shared.stopOrientationUpdates()
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,32 +675,37 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func closeCurrentItem(finished: Bool = false) {
|
func closeCurrentItem(finished: Bool = false) {
|
||||||
|
guard !closing else { return }
|
||||||
|
closing = true
|
||||||
|
|
||||||
|
if playingFullScreen { exitFullScreen() }
|
||||||
|
|
||||||
|
Delay.by(0.3) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
pause()
|
pause()
|
||||||
videoBeingOpened = nil
|
videoBeingOpened = nil
|
||||||
advancing = false
|
advancing = false
|
||||||
forceBackendOnPlay = nil
|
forceBackendOnPlay = nil
|
||||||
|
|
||||||
closing = true
|
|
||||||
controls.presentingControls = false
|
controls.presentingControls = false
|
||||||
|
|
||||||
self.prepareCurrentItemForHistory(finished: finished)
|
self.prepareCurrentItemForHistory(finished: finished)
|
||||||
|
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
Delay.by(0.8) { [weak self] in
|
Delay.by(0.7) { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.closePiP()
|
if playingInPictureInPicture { self.closePiP() }
|
||||||
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.currentItem = nil
|
self.currentItem = nil
|
||||||
}
|
}
|
||||||
self.updateNowPlayingInfo()
|
|
||||||
|
|
||||||
|
self.updateNowPlayingInfo()
|
||||||
self.backend.closeItem()
|
self.backend.closeItem()
|
||||||
self.aspectRatio = VideoPlayerView.defaultAspectRatio
|
self.aspectRatio = VideoPlayerView.defaultAspectRatio
|
||||||
self.resetAutoplay()
|
self.resetAutoplay()
|
||||||
self.closing = false
|
self.closing = false
|
||||||
self.playingFullScreen = false
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,7 +794,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toggleFullScreenAction() {
|
func toggleFullScreenAction() {
|
||||||
toggleFullscreen(playingFullScreen, showControls: false)
|
toggleFullscreen(playingFullScreen, showControls: false, initiatedByButton: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func togglePiPAction() {
|
func togglePiPAction() {
|
||||||
@ -786,20 +807,21 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var lockOrientationImage: String {
|
var lockOrientationImage: String {
|
||||||
lockedOrientation.isNil ? "lock.rotation.open" : "lock.rotation"
|
isOrientationLocked ? "lock.rotation" : "lock.rotation.open"
|
||||||
}
|
}
|
||||||
|
|
||||||
func lockOrientationAction() {
|
func lockOrientationAction() {
|
||||||
if lockedOrientation.isNil {
|
// This makes toggling orientation lock more robust
|
||||||
|
if lockedOrientation.isNil || !isOrientationLocked {
|
||||||
|
isOrientationLocked = true
|
||||||
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
|
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
|
||||||
lockedOrientation = orientationMask
|
lockedOrientation = orientationMask
|
||||||
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
Orientation.lockOrientation(orientationMask, andRotateTo: .landscapeLeft)
|
Orientation.lockOrientation(orientationMask, andRotateTo: playingFullScreen ? nil : orientation)
|
||||||
// iOS 16 workaround
|
|
||||||
Orientation.lockOrientation(orientationMask, andRotateTo: orientation)
|
|
||||||
} else {
|
} else {
|
||||||
|
isOrientationLocked = false
|
||||||
lockedOrientation = nil
|
lockedOrientation = nil
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -985,7 +1007,14 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
func handleEnterForeground() {
|
func handleEnterForeground() {
|
||||||
|
#if os(iOS)
|
||||||
|
OrientationTracker.shared.startDeviceOrientationTracking()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
// TODO: Not sure if this is realy needed on tvOS, maybe it can be removed.
|
||||||
setNeedsDrawing(presentingPlayer)
|
setNeedsDrawing(presentingPlayer)
|
||||||
|
#endif
|
||||||
|
|
||||||
if !musicMode, activeBackend == .mpv {
|
if !musicMode, activeBackend == .mpv {
|
||||||
mpvBackend.addVideoTrackFromStream()
|
mpvBackend.addVideoTrackFromStream()
|
||||||
@ -995,17 +1024,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.bindPlayerToLayer()
|
avPlayerBackend.bindPlayerToLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
if wasFullscreen {
|
|
||||||
wasFullscreen = false
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
Delay.by(0.3) {
|
|
||||||
self?.enterFullScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
|
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1018,6 +1036,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleEnterBackground() {
|
func handleEnterBackground() {
|
||||||
|
#if os(iOS)
|
||||||
|
OrientationTracker.shared.stopDeviceOrientationTracking()
|
||||||
|
#endif
|
||||||
|
|
||||||
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
|
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
|
||||||
pause()
|
pause()
|
||||||
} else if !playingInPictureInPicture, activeBackend == .appleAVPlayer {
|
} else if !playingInPictureInPicture, activeBackend == .appleAVPlayer {
|
||||||
@ -1025,15 +1047,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
} else if activeBackend == .mpv, !musicMode {
|
} else if activeBackend == .mpv, !musicMode {
|
||||||
mpvBackend.setVideoToNo()
|
mpvBackend.setVideoToNo()
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
|
||||||
guard playingFullScreen else { return }
|
|
||||||
wasFullscreen = playingFullScreen
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
Delay.by(0.3) {
|
|
||||||
self?.exitFullScreen(showControls: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1124,7 +1137,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true) {
|
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true, initiatedByButton: Bool = false) {
|
||||||
controls.presentingControls = showControls && isFullScreen
|
controls.presentingControls = showControls && isFullScreen
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@ -1139,15 +1152,13 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.controller.enterFullScreen(animated: true)
|
avPlayerBackend.controller.enterFullScreen(animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
|
let lockOrientation = rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
if currentVideoIsLandscape {
|
if currentVideoIsLandscape {
|
||||||
let delay = activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
if initiatedByButton {
|
||||||
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape)
|
||||||
Delay.by(delay) {
|
|
||||||
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interaceOrientation
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
|
||||||
}
|
}
|
||||||
|
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
|
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape, andRotateTo: orientation)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
|
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
|
||||||
@ -1155,10 +1166,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
avPlayerBackend.controller.dismiss(animated: true)
|
avPlayerBackend.controller.dismiss(animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let rotationOrientation = Constants.isIPhone ? UIInterfaceOrientation.portrait : nil
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
lockedOrientation = UIInterfaceOrientationMask.portrait
|
||||||
|
}
|
||||||
|
let rotationOrientation = Defaults[.lockPortraitWhenBrowsing] ? UIInterfaceOrientation.portrait : nil
|
||||||
|
Orientation.lockOrientation(Defaults[.lockPortraitWhenBrowsing] ? .portrait : .allButUpsideDown, andRotateTo: rotationOrientation)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,12 +93,9 @@ extension Defaults.Keys {
|
|||||||
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let isOrientationLocked = Key<Bool>("isOrientationLocked", default: Constants.isIPhone)
|
||||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: Constants.isIPhone)
|
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: Constants.isIPhone)
|
||||||
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
|
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>("rotateToLandscapeOnEnterFullScreen", default: .landscapeRight)
|
||||||
"rotateToLandscapeOnEnterFullScreen",
|
|
||||||
default: Constants.isIPhone ? .landscapeRight : .disabled
|
|
||||||
)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
||||||
@ -612,26 +609,19 @@ enum PlayerTapGestureAction: String, CaseIterable, Defaults.Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
|
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
|
||||||
case disabled
|
|
||||||
case landscapeLeft
|
case landscapeLeft
|
||||||
case landscapeRight
|
case landscapeRight
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var interaceOrientation: UIInterfaceOrientation {
|
var interfaceOrientation: UIInterfaceOrientation {
|
||||||
switch self {
|
switch self {
|
||||||
case .landscapeLeft:
|
case .landscapeLeft:
|
||||||
return .landscapeLeft
|
return .landscapeLeft
|
||||||
case .landscapeRight:
|
case .landscapeRight:
|
||||||
return .landscapeRight
|
return .landscapeRight
|
||||||
default:
|
|
||||||
return .portrait
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var isRotating: Bool {
|
|
||||||
self != .disabled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WidgetSettings: Defaults.Serializable {
|
struct WidgetSettings: Defaults.Serializable {
|
||||||
|
@ -17,12 +17,11 @@ import SwiftUI
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
|
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
|
||||||
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
|
|
||||||
if PlayerModel.shared.currentVideoIsLandscape {
|
if PlayerModel.shared.currentVideoIsLandscape {
|
||||||
let delay = PlayerModel.shared.activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
let delay = PlayerModel.shared.activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
|
||||||
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
// not sure why but first rotation call is ignore so doing rotate to same orientation first
|
||||||
Delay.by(delay) {
|
Delay.by(delay) {
|
||||||
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interaceOrientation
|
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
|
||||||
}
|
}
|
||||||
@ -37,8 +36,6 @@ import SwiftUI
|
|||||||
}
|
}
|
||||||
if !context.isCancelled {
|
if !context.isCancelled {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
self.player.lockedOrientation = nil
|
|
||||||
|
|
||||||
if Constants.isIPhone {
|
if Constants.isIPhone {
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
}
|
}
|
||||||
|
@ -389,7 +389,7 @@ struct PlayerControls: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var lockOrientationButton: some View {
|
private var lockOrientationButton: some View {
|
||||||
button("Lock Rotation", systemImage: player.lockOrientationImage, active: !player.lockedOrientation.isNil, action: player.lockOrientationAction)
|
button("Lock Rotation", systemImage: player.lockOrientationImage, active: player.isOrientationLocked, action: player.lockOrientationAction)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -64,11 +64,7 @@ extension VideoPlayerView {
|
|||||||
|
|
||||||
// Toggle fullscreen on upward drag only when not disabled
|
// Toggle fullscreen on upward drag only when not disabled
|
||||||
if verticalDrag < -50 {
|
if verticalDrag < -50 {
|
||||||
if player.playingFullScreen {
|
player.toggleFullScreenAction()
|
||||||
player.exitFullScreen(showControls: false)
|
|
||||||
} else {
|
|
||||||
player.enterFullScreen()
|
|
||||||
}
|
|
||||||
disableGestureTemporarily()
|
disableGestureTemporarily()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ struct VideoActions: View {
|
|||||||
actionButton("PiP", systemImage: player.pipImage, active: player.playingInPictureInPicture, action: player.togglePiPAction)
|
actionButton("PiP", systemImage: player.pipImage, active: player.playingInPictureInPicture, action: player.togglePiPAction)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
case .lockOrientation:
|
case .lockOrientation:
|
||||||
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.lockedOrientation != nil, action: player.lockOrientationAction)
|
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.isOrientationLocked, action: player.lockOrientationAction)
|
||||||
#endif
|
#endif
|
||||||
case .restart:
|
case .restart:
|
||||||
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
|
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
|
||||||
|
@ -111,9 +111,6 @@ struct VideoPlayerView: View {
|
|||||||
.onChange(of: geometry.size) { _ in
|
.onChange(of: geometry.size) { _ in
|
||||||
self.playerSize = geometry.size
|
self.playerSize = geometry.size
|
||||||
}
|
}
|
||||||
.onChange(of: fullScreenDetails) { value in
|
|
||||||
player.backend.setNeedsDrawing(!value)
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.onChange(of: player.presentingPlayer) { newValue in
|
.onChange(of: player.presentingPlayer) { newValue in
|
||||||
if newValue {
|
if newValue {
|
||||||
@ -127,19 +124,6 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
viewDragOffset = 0
|
viewDragOffset = 0
|
||||||
|
|
||||||
Delay.by(0.2) {
|
|
||||||
orientationModel.configureOrientationUpdatesBasedOnAccelerometer()
|
|
||||||
|
|
||||||
if let orientationMask = player.lockedOrientation {
|
|
||||||
Orientation.lockOrientation(
|
|
||||||
orientationMask,
|
|
||||||
andRotateTo: orientationMask == .landscapeLeft ? .landscapeLeft : orientationMask == .landscapeRight ? .landscapeRight : .portrait
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onAnimationCompleted(for: viewDragOffset) {
|
.onAnimationCompleted(for: viewDragOffset) {
|
||||||
guard !dragGestureState else { return }
|
guard !dragGestureState else { return }
|
||||||
@ -313,11 +297,14 @@ struct VideoPlayerView: View {
|
|||||||
playerSize: player.playerSize,
|
playerSize: player.playerSize,
|
||||||
fullScreen: fullScreenDetails
|
fullScreen: fullScreenDetails
|
||||||
))
|
))
|
||||||
|
#if os(macOS)
|
||||||
|
// TODO: Check whether this is needed on macOS.
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if player.presentingPlayer {
|
if player.presentingPlayer {
|
||||||
player.setNeedsDrawing(true)
|
player.setNeedsDrawing(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
.id(player.currentVideo?.cacheKey)
|
.id(player.currentVideo?.cacheKey)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,6 +10,7 @@ struct BrowsingSettings: View {
|
|||||||
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
||||||
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
||||||
@Default(.showDocuments) private var showDocuments
|
@Default(.showDocuments) private var showDocuments
|
||||||
#endif
|
#endif
|
||||||
@ -161,14 +162,18 @@ struct BrowsingSettings: View {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Toggle("Show Documents", isOn: $showDocuments)
|
Toggle("Show Documents", isOn: $showDocuments)
|
||||||
|
|
||||||
|
if Constants.isIPad {
|
||||||
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
|
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
|
||||||
.onChange(of: lockPortraitWhenBrowsing) { lock in
|
.onChange(of: lockPortraitWhenBrowsing) { lock in
|
||||||
if lock {
|
if lock {
|
||||||
|
enterFullscreenInLandscape = true
|
||||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
} else {
|
} else {
|
||||||
|
enterFullscreenInLandscape = false
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if !accounts.isEmpty {
|
if !accounts.isEmpty {
|
||||||
|
@ -18,8 +18,8 @@ struct PlayerSettings: View {
|
|||||||
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
||||||
@Default(.closeVideoOnEOF) private var closeVideoOnEOF
|
@Default(.closeVideoOnEOF) private var closeVideoOnEOF
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
|
||||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
|
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
||||||
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
|
||||||
#endif
|
#endif
|
||||||
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
||||||
@ -87,7 +87,7 @@ struct PlayerSettings: View {
|
|||||||
}
|
}
|
||||||
pauseOnHidingPlayerToggle
|
pauseOnHidingPlayerToggle
|
||||||
closeVideoOnEOFToggle
|
closeVideoOnEOFToggle
|
||||||
#if !os(tvOS)
|
#if os(macOS)
|
||||||
exitFullscreenOnEOFToggle
|
exitFullscreenOnEOFToggle
|
||||||
#endif
|
#endif
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
@ -202,11 +202,12 @@ struct PlayerSettings: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Section(header: SettingsHeader(text: "Orientation".localized())) {
|
Section(header: SettingsHeader(text: "Fullscreen".localized())) {
|
||||||
if idiom == .pad {
|
if Constants.isIPad {
|
||||||
enterFullscreenInLandscapeToggle
|
enterFullscreenInLandscapeToggle
|
||||||
}
|
}
|
||||||
honorSystemOrientationLockToggle
|
|
||||||
|
exitFullscreenOnEOFToggle
|
||||||
rotateToLandscapeOnEnterFullScreenPicker
|
rotateToLandscapeOnEnterFullScreenPicker
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -318,20 +319,15 @@ struct PlayerSettings: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var honorSystemOrientationLockToggle: some View {
|
|
||||||
Toggle("Honor orientation lock", isOn: $honorSystemOrientationLock)
|
|
||||||
.disabled(!enterFullscreenInLandscape)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var enterFullscreenInLandscapeToggle: some View {
|
private var enterFullscreenInLandscapeToggle: some View {
|
||||||
Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
|
Toggle("Enter fullscreen in landscape orientation", isOn: $enterFullscreenInLandscape)
|
||||||
|
.disabled(lockPortraitWhenBrowsing)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var rotateToLandscapeOnEnterFullScreenPicker: some View {
|
private var rotateToLandscapeOnEnterFullScreenPicker: some View {
|
||||||
Picker("Rotate when entering fullscreen on landscape video", selection: $rotateToLandscapeOnEnterFullScreen) {
|
Picker("Default orientation", selection: $rotateToLandscapeOnEnterFullScreen) {
|
||||||
Text("Landscape left").tag(FullScreenRotationSetting.landscapeLeft)
|
Text("Landscape left").tag(FullScreenRotationSetting.landscapeLeft)
|
||||||
Text("Landscape right").tag(FullScreenRotationSetting.landscapeRight)
|
Text("Landscape right").tag(FullScreenRotationSetting.landscapeRight)
|
||||||
Text("No rotation").tag(FullScreenRotationSetting.disabled)
|
|
||||||
}
|
}
|
||||||
.modifier(SettingsPickerModifier())
|
.modifier(SettingsPickerModifier())
|
||||||
}
|
}
|
||||||
|
@ -204,9 +204,14 @@ struct YatteeApp: App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
if Defaults[.lockPortraitWhenBrowsing] {
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
Orientation.lockOrientation(.all, andRotateTo: .portrait)
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else {
|
||||||
|
let rotationOrientation =
|
||||||
|
OrientationTracker.shared.currentDeviceOrientation.rawValue == 4 ? UIInterfaceOrientation.landscapeRight :
|
||||||
|
(OrientationTracker.shared.currentDeviceOrientation.rawValue == 3 ? UIInterfaceOrientation.landscapeLeft : UIInterfaceOrientation.portrait)
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -225,6 +230,17 @@ struct YatteeApp: App {
|
|||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
self.migrateQualityProfiles()
|
self.migrateQualityProfiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.migrateRotateToLandscapeOnEnterFullScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
self.migrateLockPortraitWhenBrowsing()
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +269,22 @@ struct YatteeApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
func migrateRotateToLandscapeOnEnterFullScreen() {
|
||||||
|
if Defaults[.rotateToLandscapeOnEnterFullScreen] != .landscapeRight || Defaults[.rotateToLandscapeOnEnterFullScreen] != .landscapeLeft {
|
||||||
|
Defaults[.rotateToLandscapeOnEnterFullScreen] = .landscapeRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateLockPortraitWhenBrowsing() {
|
||||||
|
if Constants.isIPhone {
|
||||||
|
Defaults[.lockPortraitWhenBrowsing] = true
|
||||||
|
} else if Constants.isIPad, Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
Defaults[.enterFullscreenInLandscape] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var navigationStyle: NavigationStyle {
|
var navigationStyle: NavigationStyle {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
return horizontalSizeClass == .compact ? .tab : .sidebar
|
return horizontalSizeClass == .compact ? .tab : .sidebar
|
||||||
|
@ -4366,7 +4366,9 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = "";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -4415,7 +4417,9 @@
|
|||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||||
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
INFOPLIST_KEY_UIStatusBarHidden = NO;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UIStatusBarStyle = "";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
import Logging
|
import Logging
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
var orientationLock = UIInterfaceOrientationMask.all
|
var orientationLock = UIInterfaceOrientationMask.allButUpsideDown
|
||||||
|
|
||||||
private var logger = Logger(label: "stream.yattee.app.delegalate")
|
private var logger = Logger(label: "stream.yattee.app.delegate")
|
||||||
private(set) static var instance: AppDelegate!
|
private(set) static var instance: AppDelegate!
|
||||||
|
|
||||||
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
|
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
|
||||||
orientationLock
|
return orientationLock
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection
|
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection
|
||||||
@ -19,6 +20,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
UIViewController.swizzleHomeIndicatorProperty()
|
UIViewController.swizzleHomeIndicatorProperty()
|
||||||
OrientationTracker.shared.startDeviceOrientationTracking()
|
OrientationTracker.shared.startDeviceOrientationTracking()
|
||||||
|
OrientationModel.shared.startOrientationUpdates()
|
||||||
|
|
||||||
// Configure the audio session for playback
|
// Configure the audio session for playback
|
||||||
do {
|
do {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import Defaults
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Logging
|
||||||
import Repeat
|
import Repeat
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
final class OrientationModel {
|
final class OrientationModel {
|
||||||
static var shared = OrientationModel()
|
static var shared = OrientationModel()
|
||||||
|
let logger = Logger(label: "stream.yattee.orientation.model")
|
||||||
|
|
||||||
var orientation = UIInterfaceOrientation.portrait
|
var orientation = UIInterfaceOrientation.portrait
|
||||||
var lastOrientation: UIInterfaceOrientation?
|
var lastOrientation: UIInterfaceOrientation?
|
||||||
@ -13,79 +15,69 @@ final class OrientationModel {
|
|||||||
|
|
||||||
private var player = PlayerModel.shared
|
private var player = PlayerModel.shared
|
||||||
|
|
||||||
func configureOrientationUpdatesBasedOnAccelerometer() {
|
func startOrientationUpdates() {
|
||||||
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
|
// Ensure the orientation observer is active
|
||||||
if currentOrientation.isLandscape,
|
|
||||||
Defaults[.enterFullscreenInLandscape],
|
|
||||||
!Defaults[.honorSystemOrientationLock],
|
|
||||||
!player.playingFullScreen,
|
|
||||||
!player.currentItem.isNil,
|
|
||||||
player.lockedOrientation.isNil || player.lockedOrientation!.contains(.landscape),
|
|
||||||
!player.playingInPictureInPicture,
|
|
||||||
player.presentingPlayer
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.player.controls.presentingControls = false
|
|
||||||
self.player.enterFullScreen(showControls: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
player.onPresentPlayer.append {
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: currentOrientation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
orientationObserver = NotificationCenter.default.addObserver(
|
orientationObserver = NotificationCenter.default.addObserver(
|
||||||
forName: OrientationTracker.deviceOrientationChangedNotification,
|
forName: OrientationTracker.deviceOrientationChangedNotification,
|
||||||
object: nil,
|
object: nil,
|
||||||
queue: .main
|
queue: .main
|
||||||
) { _ in
|
) { _ in
|
||||||
guard !Defaults[.honorSystemOrientationLock],
|
self.logger.info("Notification received: Device orientation changed.")
|
||||||
self.player.presentingPlayer,
|
|
||||||
!self.player.playingInPictureInPicture,
|
// We only allow .portrait and are not showing the player
|
||||||
self.player.lockedOrientation.isNil
|
guard (!self.player.presentingPlayer && !Defaults[.lockPortraitWhenBrowsing]) || self.player.presentingPlayer
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
|
self.logger.info("Current interface orientation: \(orientation)")
|
||||||
|
|
||||||
guard self.lastOrientation != orientation else {
|
// Always update lastOrientation to keep track of the latest state
|
||||||
return
|
if self.lastOrientation != orientation {
|
||||||
|
self.lastOrientation = orientation
|
||||||
|
self.logger.info("Orientation changed to: \(orientation)")
|
||||||
|
} else {
|
||||||
|
self.logger.info("Orientation has not changed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lastOrientation = orientation
|
// Only take action if the player is active and presenting
|
||||||
|
guard (!self.player.isOrientationLocked && !self.player.playingInPictureInPicture) || (!Defaults[.lockPortraitWhenBrowsing] && !self.player.presentingPlayer) || (!Defaults[.lockPortraitWhenBrowsing] && self.player.presentingPlayer && !self.player.isOrientationLocked)
|
||||||
|
else {
|
||||||
|
self.logger.info("Only updating orientation without actions.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard Defaults[.enterFullscreenInLandscape],
|
|
||||||
self.player.presentingPlayer
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.orientationDebouncer.callback = {
|
self.orientationDebouncer.callback = {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if orientation.isLandscape {
|
if orientation.isLandscape {
|
||||||
|
if Defaults[.enterFullscreenInLandscape], self.player.presentingPlayer {
|
||||||
|
self.logger.info("Entering fullscreen because orientation is landscape.")
|
||||||
self.player.controls.presentingControls = false
|
self.player.controls.presentingControls = false
|
||||||
self.player.enterFullScreen(showControls: false)
|
self.player.enterFullScreen(showControls: false)
|
||||||
|
}
|
||||||
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
} else {
|
} else {
|
||||||
|
self.logger.info("Exiting fullscreen because orientation is portrait.")
|
||||||
|
if self.player.playingFullScreen {
|
||||||
self.player.exitFullScreen(showControls: false)
|
self.player.exitFullScreen(showControls: false)
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
}
|
||||||
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
|
} else {
|
||||||
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.orientationDebouncer.call()
|
self.orientationDebouncer.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopOrientationUpdates() {
|
|
||||||
guard let observer = orientationObserver else { return }
|
|
||||||
NotificationCenter.default.removeObserver(observer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
||||||
|
logger.info("Locking orientation to: \(orientation), rotating to: \(String(describing: rotateOrientation)).")
|
||||||
if let rotateOrientation {
|
if let rotateOrientation {
|
||||||
self.orientation = rotateOrientation
|
self.orientation = rotateOrientation
|
||||||
lastOrientation = rotateOrientation
|
lastOrientation = rotateOrientation
|
||||||
|
Loading…
Reference in New Issue
Block a user