Compare commits

...

12 Commits

Author SHA1 Message Date
Arkadiusz Fal
b0264aaabe Bump build number to 195 2024-09-05 23:01:29 +02:00
Arkadiusz Fal
035f3503c4 Update CHANGELOG 2024-09-05 23:01:19 +02:00
Arkadiusz Fal
e3ac11c172 Merge pull request #786 from stonerl/simplified-fullscreen-and-orientation
iOS: Simplified fullscreen and orientation
2024-09-05 22:59:54 +02:00
Arkadiusz Fal
7aed6ac0d9 Merge pull request #799 from stonerl/controls-background
player controls: add background opacity selection
2024-09-05 22:54:30 +02:00
Arkadiusz Fal
457c0ce7b3 Merge pull request #797 from stonerl/shorts-resolutions
add missing Shorts resolutions
2024-09-05 22:53:42 +02:00
Arkadiusz Fal
747baf3edd Merge pull request #801 from stonerl/O2-for-macOS
use -O1 on macOS
2024-09-05 22:53:26 +02:00
Arkadiusz Fal
cd24a0322f Merge pull request #802 from stonerl/buttons-interfere-with-search
macOS: only apply player shortcuts when window is active
2024-09-05 22:53:16 +02:00
Toni Förster
d525a22215 macOS only apply player shortcuts when window is active 2024-09-05 21:53:25 +02:00
Toni Förster
322a550666 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>
2024-09-05 18:17:14 +02:00
Toni Förster
98fa0b98e5 use -O1 on macOS
On macOS optimisation level -O3 seems to be a bit aggressive and can cause crashes when opening MPV.

- fixes #783

Signed-off-by: Toni Förster <toni.foerster@gmail.com>
2024-09-05 17:35:52 +02:00
Toni Förster
5313e4ead0 player controls: add background opacity selection
Signed-off-by: Toni Förster <toni.foerster@gmail.com>
2024-09-05 15:14:39 +02:00
Toni Förster
fa7b897e76 add missing Shorts resolutions
Signed-off-by: Toni Förster <toni.foerster@gmail.com>
2024-09-04 12:44:43 +02:00
21 changed files with 275 additions and 204 deletions

View File

@@ -1,11 +1,9 @@
## Build 194
* Gestures: swipe up toggles fullscreen by @stonerl in https://github.com/yattee/yattee/pull/778
* dont open video when dismissing context menu by @stonerl in https://github.com/yattee/yattee/pull/780
* mpv: remove video layer when entering background by @stonerl in https://github.com/yattee/yattee/pull/793
* hi-res invidious logos by @stonerl in https://github.com/yattee/yattee/pull/791
* enable -O3 by @stonerl in https://github.com/yattee/yattee/pull/794
* Better audio ducking by @stonerl in https://github.com/yattee/yattee/pull/779
* fix picture in picture by @stonerl in https://github.com/yattee/yattee/pull/789
## Build 195
* iOS: Simplified fullscreen and orientation by @stonerl in https://github.com/yattee/yattee/pull/786
* macOS: only apply player shortcuts when window is active by @stonerl in https://github.com/yattee/yattee/pull/802
* player controls: add background opacity selection by @stonerl in https://github.com/yattee/yattee/pull/799
* add missing Shorts resolutions by @stonerl in https://github.com/yattee/yattee/pull/797
* use -O1 on macOS by @stonerl in https://github.com/yattee/yattee/pull/801
## Previous builds
* Add skip, play/pause, and fullscreen shortcuts to macOS player (by @rickykresslein)
@@ -24,6 +22,13 @@
* Add import export of missing settings
* macOS: Fix settings windows layout
* Fix seek OSD layout on tvOS, revert OSD position
* Gestures: swipe up toggles fullscreen by @stonerl in https://github.com/yattee/yattee/pull/778
* dont open video when dismissing context menu by @stonerl in https://github.com/yattee/yattee/pull/780
* mpv: remove video layer when entering background by @stonerl in https://github.com/yattee/yattee/pull/793
* hi-res invidious logos by @stonerl in https://github.com/yattee/yattee/pull/791
* enable -O3 by @stonerl in https://github.com/yattee/yattee/pull/794
* Better audio ducking by @stonerl in https://github.com/yattee/yattee/pull/779
* fix picture in picture by @stonerl in https://github.com/yattee/yattee/pull/789
* Invidious: propper HTTP basic auth support by @stonerl in https://github.com/yattee/yattee/pull/762
* Apply correct orientation by @stonerl in https://github.com/yattee/yattee/pull/770
* Circular Invidious logo by @stonerl in https://github.com/yattee/yattee/pull/769

View File

@@ -10,6 +10,7 @@ final class ConstrolsSettingsGroupExporter: SettingsGroupExporter {
"seekGestureSpeed": Defaults[.seekGestureSpeed],
"playerControlsLayout": Defaults[.playerControlsLayout].rawValue,
"fullScreenPlayerControlsLayout": Defaults[.fullScreenPlayerControlsLayout].rawValue,
"playerControlsBackgroundOpacity": Defaults[.playerControlsBackgroundOpacity],
"systemControlsCommands": Defaults[.systemControlsCommands].rawValue,
"buttonBackwardSeekDuration": Defaults[.buttonBackwardSeekDuration],
"buttonForwardSeekDuration": Defaults[.buttonForwardSeekDuration],

View File

@@ -44,7 +44,7 @@ final class PlayerSettingsGroupExporter: SettingsGroupExporter {
#endif
#if os(iOS)
export["honorSystemOrientationLock"].bool = Defaults[.honorSystemOrientationLock]
export["isOrientationLocked"].bool = Defaults[.isOrientationLocked]
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
#endif

View File

@@ -33,6 +33,10 @@ struct ConstrolsSettingsGroupImporter {
Defaults[.fullScreenPlayerControlsLayout] = fullScreenPlayerControlsLayout
}
if let playerControlsBackgroundOpacity = json["playerControlsBackgroundOpacity"].double {
Defaults[.playerControlsBackgroundOpacity] = playerControlsBackgroundOpacity
}
if let systemControlsCommandsString = json["systemControlsCommands"].string,
let systemControlsCommands = SystemControlsCommands(rawValue: systemControlsCommandsString)
{

View File

@@ -97,8 +97,8 @@ struct PlayerSettingsGroupImporter {
#endif
#if os(iOS)
if let honorSystemOrientationLock = json["honorSystemOrientationLock"].bool {
Defaults[.honorSystemOrientationLock] = honorSystemOrientationLock
if let isOrientationLocked = json["isOrientationLocked"].bool {
Defaults[.isOrientationLocked] = isOrientationLocked
}
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {

View File

@@ -14,6 +14,8 @@ final class MPVClient: ObservableObject {
}
private var logger = Logger(label: "mpv-client")
private var needsDrawingCooldown = false
private var needsDrawingWorkItem: DispatchWorkItem?
var mpv: OpaquePointer!
var mpvGL: OpaquePointer!
@@ -389,10 +391,30 @@ final class MPVClient: ObservableObject {
}
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)")
// Set the cooldown flag to true and cancel any existing work item
needsDrawingCooldown = true
needsDrawingWorkItem?.cancel()
#if !os(macOS)
glView?.needsDrawing = needsDrawing
#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(

View File

@@ -56,7 +56,6 @@ final class PlayerModel: ObservableObject {
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
@Published var activeBackend = PlayerBackendType.mpv
@Published var forceBackendOnPlay: PlayerBackendType?
@Published var wasFullscreen = false
var avPlayerBackend = AVPlayerBackend()
var mpvBackend = MPVBackend()
@@ -131,6 +130,12 @@ final class PlayerModel: ObservableObject {
#if os(iOS)
@Published var lockedOrientation: UIInterfaceOrientationMask?
@Published var isOrientationLocked: Bool {
didSet {
Defaults[.isOrientationLocked] = isOrientationLocked
}
}
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
#endif
@@ -201,6 +206,16 @@ final class PlayerModel: ObservableObject {
#endif
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)
mpvBackend.controller = mpvController
mpvBackend.client = mpvController.client
@@ -517,7 +532,10 @@ final class PlayerModel: ObservableObject {
}
private func handlePresentationChange() {
backend.setNeedsDrawing(presentingPlayer)
#if !os(iOS)
// TODO: Check whether this is neede on tvOS and macOS
backend.setNeedsDrawing(presentingPlayer)
#endif
#if os(iOS)
if presentingPlayer, activeBackend == .appleAVPlayer, avPlayerUsesSystemControls, Constants.isIPhone {
@@ -551,8 +569,6 @@ final class PlayerModel: ObservableObject {
} else {
Orientation.lockOrientation(.allButUpsideDown)
}
OrientationModel.shared.stopOrientationUpdates()
#endif
}
}
@@ -659,32 +675,37 @@ final class PlayerModel: ObservableObject {
}
func closeCurrentItem(finished: Bool = false) {
pause()
videoBeingOpened = nil
advancing = false
forceBackendOnPlay = nil
guard !closing else { return }
closing = true
controls.presentingControls = false
self.prepareCurrentItemForHistory(finished: finished)
if playingFullScreen { exitFullScreen() }
self.hide()
Delay.by(0.8) { [weak self] in
Delay.by(0.3) { [weak self] in
guard let self else { return }
self.closePiP()
pause()
videoBeingOpened = nil
advancing = false
forceBackendOnPlay = nil
withAnimation {
self.currentItem = nil
controls.presentingControls = false
self.prepareCurrentItemForHistory(finished: finished)
self.hide()
Delay.by(0.7) { [weak self] in
guard let self else { return }
if playingInPictureInPicture { self.closePiP() }
withAnimation {
self.currentItem = nil
}
self.updateNowPlayingInfo()
self.backend.closeItem()
self.aspectRatio = VideoPlayerView.defaultAspectRatio
self.resetAutoplay()
self.closing = false
}
self.updateNowPlayingInfo()
self.backend.closeItem()
self.aspectRatio = VideoPlayerView.defaultAspectRatio
self.resetAutoplay()
self.closing = false
self.playingFullScreen = false
}
}
@@ -773,7 +794,7 @@ final class PlayerModel: ObservableObject {
}
func toggleFullScreenAction() {
toggleFullscreen(playingFullScreen, showControls: false)
toggleFullscreen(playingFullScreen, showControls: false, initiatedByButton: true)
}
func togglePiPAction() {
@@ -786,20 +807,21 @@ final class PlayerModel: ObservableObject {
#if os(iOS)
var lockOrientationImage: String {
lockedOrientation.isNil ? "lock.rotation.open" : "lock.rotation"
isOrientationLocked ? "lock.rotation" : "lock.rotation.open"
}
func lockOrientationAction() {
if lockedOrientation.isNil {
// This makes toggling orientation lock more robust
if lockedOrientation.isNil || !isOrientationLocked {
isOrientationLocked = true
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
lockedOrientation = orientationMask
let orientation = OrientationTracker.shared.currentInterfaceOrientation
Orientation.lockOrientation(orientationMask, andRotateTo: .landscapeLeft)
// iOS 16 workaround
Orientation.lockOrientation(orientationMask, andRotateTo: orientation)
Orientation.lockOrientation(orientationMask, andRotateTo: playingFullScreen ? nil : orientation)
} else {
isOrientationLocked = false
lockedOrientation = nil
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
Orientation.lockOrientation(.allButUpsideDown)
}
}
#endif
@@ -985,7 +1007,14 @@ final class PlayerModel: ObservableObject {
}
#else
func handleEnterForeground() {
setNeedsDrawing(presentingPlayer)
#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)
#endif
if !musicMode, activeBackend == .mpv {
mpvBackend.addVideoTrackFromStream()
@@ -995,17 +1024,6 @@ final class PlayerModel: ObservableObject {
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 {
return
}
@@ -1018,6 +1036,10 @@ final class PlayerModel: ObservableObject {
}
func handleEnterBackground() {
#if os(iOS)
OrientationTracker.shared.stopDeviceOrientationTracking()
#endif
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
pause()
} else if !playingInPictureInPicture, activeBackend == .appleAVPlayer {
@@ -1025,15 +1047,6 @@ final class PlayerModel: ObservableObject {
} else if activeBackend == .mpv, !musicMode {
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
@@ -1124,7 +1137,7 @@ final class PlayerModel: ObservableObject {
task.resume()
}
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true) {
func toggleFullscreen(_ isFullScreen: Bool, showControls: Bool = true, initiatedByButton: Bool = false) {
controls.presentingControls = showControls && isFullScreen
#if os(macOS)
@@ -1139,15 +1152,13 @@ final class PlayerModel: ObservableObject {
avPlayerBackend.controller.enterFullScreen(animated: true)
return
}
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
let lockOrientation = rotateToLandscapeOnEnterFullScreen.interfaceOrientation
if currentVideoIsLandscape {
let delay = activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
// not sure why but first rotation call is ignore so doing rotate to same orientation first
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)
if initiatedByButton {
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape)
}
let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interfaceOrientation
Orientation.lockOrientation(self.isOrientationLocked ? (lockOrientation == .landscapeRight ? .landscapeRight : .landscapeLeft) : .landscape, andRotateTo: orientation)
}
} else {
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
@@ -1155,10 +1166,12 @@ final class PlayerModel: ObservableObject {
avPlayerBackend.controller.dismiss(animated: true)
return
}
let rotationOrientation = Constants.isIPhone ? UIInterfaceOrientation.portrait : nil
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: rotationOrientation)
if Defaults[.lockPortraitWhenBrowsing] {
lockedOrientation = UIInterfaceOrientationMask.portrait
}
let rotationOrientation = Defaults[.lockPortraitWhenBrowsing] ? UIInterfaceOrientation.portrait : nil
Orientation.lockOrientation(Defaults[.lockPortraitWhenBrowsing] ? .portrait : .allButUpsideDown, andRotateTo: rotationOrientation)
}
#endif
}
@@ -1286,7 +1299,10 @@ final class PlayerModel: ObservableObject {
#if os(macOS)
private func assignKeyPressMonitor() {
keyPressMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { keyEvent -> NSEvent? in
keyPressMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] keyEvent -> NSEvent? in
// Check if the player window is the key window
guard let self, let window = Windows.playerWindow, window.isKeyWindow else { return keyEvent }
switch keyEvent.keyCode {
case 124:
if !self.liveStreamInAVPlayer {

View File

@@ -139,10 +139,14 @@ class Stream: Equatable, Hashable, Identifiable {
case sd428p30
case sd428p25
case sd426p30
case sd426p25
case sd360p30
case sd360p25
case sd320p30
case sd320p25
case sd256p30
case sd256p25
case sd240p30
case sd240p25
case sd214p30
@@ -253,7 +257,7 @@ class Stream: Equatable, Hashable, Identifiable {
case .sd480p30, .sd480p25:
return 2_500_000 // 2.5 Mbit/s
case .sd428p30, .sd428p25:
case .sd428p30, .sd428p25, .sd426p30, .sd426p25:
return 2_000_000 // 2 Mbit/s
case .sd360p30, .sd360p25:
@@ -262,7 +266,7 @@ class Stream: Equatable, Hashable, Identifiable {
case .sd320p30, .sd320p25:
return 1_200_000 // 1.2 Mbit/s
case .sd240p30, .sd240p25:
case .sd256p30, .sd256p25, .sd240p30, .sd240p25:
return 1_000_000 // 1 Mbit/s
case .sd214p30, .sd214p25:

View File

@@ -93,12 +93,9 @@ extension Defaults.Keys {
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
#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 rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
"rotateToLandscapeOnEnterFullScreen",
default: Constants.isIPhone ? .landscapeRight : .disabled
)
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>("rotateToLandscapeOnEnterFullScreen", default: .landscapeRight)
#endif
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
@@ -134,6 +131,7 @@ extension Defaults.Keys {
static let playerControlsLayout = Key<PlayerControlsLayout>("playerControlsLayout", default: playerControlsLayoutDefault)
static let fullScreenPlayerControlsLayout = Key<PlayerControlsLayout>("fullScreenPlayerControlsLayout", default: fullScreenPlayerControlsLayoutDefault)
static let playerControlsBackgroundOpacity = Key<Double>("playerControlsBackgroundOpacity", default: 0.2)
static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
@@ -612,26 +610,19 @@ enum PlayerTapGestureAction: String, CaseIterable, Defaults.Serializable {
}
enum FullScreenRotationSetting: String, CaseIterable, Defaults.Serializable {
case disabled
case landscapeLeft
case landscapeRight
#if os(iOS)
var interaceOrientation: UIInterfaceOrientation {
var interfaceOrientation: UIInterfaceOrientation {
switch self {
case .landscapeLeft:
return .landscapeLeft
case .landscapeRight:
return .landscapeRight
default:
return .portrait
}
}
#endif
var isRotating: Bool {
self != .disabled
}
}
struct WidgetSettings: Defaults.Serializable {

View File

@@ -17,12 +17,11 @@ import SwiftUI
#if os(iOS)
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
if PlayerModel.shared.currentVideoIsLandscape {
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
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: orientation)
}
@@ -37,8 +36,6 @@ import SwiftUI
}
if !context.isCancelled {
#if os(iOS)
self.player.lockedOrientation = nil
if Constants.isIPhone {
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
}

View File

@@ -29,6 +29,7 @@ struct PlayerControls: View {
@Default(.playerControlsLayout) private var regularPlayerControlsLayout
@Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout
@Default(.playerControlsBackgroundOpacity) private var playerControlsBackgroundOpacity
@Default(.buttonBackwardSeekDuration) private var buttonBackwardSeekDuration
@Default(.buttonForwardSeekDuration) private var buttonForwardSeekDuration
@@ -270,6 +271,9 @@ struct PlayerControls: View {
}
} else if player.videoForDisplay == nil {
Color.black
} else if model.presentingControls {
Color.black.opacity(playerControlsBackgroundOpacity)
.edgesIgnoringSafeArea(.all)
}
}
}
@@ -389,7 +393,7 @@ struct PlayerControls: View {
#if os(iOS)
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

View File

@@ -64,11 +64,7 @@ extension VideoPlayerView {
// Toggle fullscreen on upward drag only when not disabled
if verticalDrag < -50 {
if player.playingFullScreen {
player.exitFullScreen(showControls: false)
} else {
player.enterFullScreen()
}
player.toggleFullScreenAction()
disableGestureTemporarily()
return
}

View File

@@ -158,7 +158,7 @@ struct VideoActions: View {
actionButton("PiP", systemImage: player.pipImage, active: player.playingInPictureInPicture, action: player.togglePiPAction)
#if os(iOS)
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
case .restart:
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)

View File

@@ -111,9 +111,6 @@ struct VideoPlayerView: View {
.onChange(of: geometry.size) { _ in
self.playerSize = geometry.size
}
.onChange(of: fullScreenDetails) { value in
player.backend.setNeedsDrawing(!value)
}
#if os(iOS)
.onChange(of: player.presentingPlayer) { newValue in
if newValue {
@@ -127,19 +124,6 @@ struct VideoPlayerView: View {
}
#endif
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) {
guard !dragGestureState else { return }
@@ -313,11 +297,14 @@ struct VideoPlayerView: View {
playerSize: player.playerSize,
fullScreen: fullScreenDetails
))
#if os(macOS)
// TODO: Check whether this is needed on macOS.
.onDisappear {
if player.presentingPlayer {
player.setNeedsDrawing(true)
}
}
#endif
.id(player.currentVideo?.cacheKey)
.transition(.opacity)
} else {

View File

@@ -10,6 +10,7 @@ struct BrowsingSettings: View {
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
#if os(iOS)
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
@Default(.showDocuments) private var showDocuments
#endif
@@ -161,14 +162,18 @@ struct BrowsingSettings: View {
#if os(iOS)
Toggle("Show Documents", isOn: $showDocuments)
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
.onChange(of: lockPortraitWhenBrowsing) { lock in
if lock {
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
} else {
Orientation.lockOrientation(.allButUpsideDown)
if Constants.isIPad {
Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing)
.onChange(of: lockPortraitWhenBrowsing) { lock in
if lock {
enterFullscreenInLandscape = true
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
} else {
enterFullscreenInLandscape = false
Orientation.lockOrientation(.allButUpsideDown)
}
}
}
}
#endif
if !accounts.isEmpty {

View File

@@ -38,6 +38,7 @@ struct PlayerControlsSettings: View {
@Default(.playerControlsAdvanceToNextEnabled) private var playerControlsAdvanceToNextEnabled
@Default(.playerControlsPlaybackModeEnabled) private var playerControlsPlaybackModeEnabled
@Default(.playerControlsMusicModeEnabled) private var playerControlsMusicModeEnabled
@Default(.playerControlsBackgroundOpacity) private var playerControlsBackgroundOpacity
private var player = PlayerModel.shared
@@ -76,6 +77,8 @@ struct PlayerControlsSettings: View {
playerControlsLayoutPicker
SettingsHeader(text: "Fullscreen size".localized(), secondary: true)
fullScreenPlayerControlsLayoutPicker
SettingsHeader(text: "Background opacity".localized(), secondary: true)
playerControlsBackgroundOpacityPicker
}
#endif
@@ -202,6 +205,15 @@ struct PlayerControlsSettings: View {
.modifier(SettingsPickerModifier())
}
private var playerControlsBackgroundOpacityPicker: some View {
Picker("Background opacity", selection: $playerControlsBackgroundOpacity) {
ForEach(Array(stride(from: 0.0, through: 1.0, by: 0.1)), id: \.self) { value in
Text("\(Int(value * 100))%").tag(value)
}
}
.modifier(SettingsPickerModifier())
}
@ViewBuilder private var seekingSection: some View {
seekingDurationSetting("System controls", $systemControlsSeekDuration)
.foregroundColor(systemControlsCommands == .restartAndAdvanceToNext ? .secondary : .primary)

View File

@@ -18,8 +18,8 @@ struct PlayerSettings: View {
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
@Default(.closeVideoOnEOF) private var closeVideoOnEOF
#if os(iOS)
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
@Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
#endif
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
@@ -87,7 +87,7 @@ struct PlayerSettings: View {
}
pauseOnHidingPlayerToggle
closeVideoOnEOFToggle
#if !os(tvOS)
#if os(macOS)
exitFullscreenOnEOFToggle
#endif
#if !os(macOS)
@@ -202,11 +202,12 @@ struct PlayerSettings: View {
#endif
#if os(iOS)
Section(header: SettingsHeader(text: "Orientation".localized())) {
if idiom == .pad {
Section(header: SettingsHeader(text: "Fullscreen".localized())) {
if Constants.isIPad {
enterFullscreenInLandscapeToggle
}
honorSystemOrientationLockToggle
exitFullscreenOnEOFToggle
rotateToLandscapeOnEnterFullScreenPicker
}
#endif
@@ -318,20 +319,15 @@ struct PlayerSettings: View {
#endif
#if os(iOS)
private var honorSystemOrientationLockToggle: some View {
Toggle("Honor orientation lock", isOn: $honorSystemOrientationLock)
.disabled(!enterFullscreenInLandscape)
}
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 {
Picker("Rotate when entering fullscreen on landscape video", selection: $rotateToLandscapeOnEnterFullScreen) {
Picker("Default orientation", selection: $rotateToLandscapeOnEnterFullScreen) {
Text("Landscape left").tag(FullScreenRotationSetting.landscapeLeft)
Text("Landscape right").tag(FullScreenRotationSetting.landscapeRight)
Text("No rotation").tag(FullScreenRotationSetting.disabled)
}
.modifier(SettingsPickerModifier())
}

View File

@@ -204,9 +204,14 @@ struct YatteeApp: App {
}
#if os(iOS)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
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
@@ -225,6 +230,17 @@ struct YatteeApp: App {
DispatchQueue.global(qos: .userInitiated).async {
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 {
#if os(iOS)
return horizontalSizeClass == .compact ? .tab : .sidebar

View File

@@ -4103,7 +4103,7 @@
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
@@ -4134,7 +4134,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist";
@@ -4165,7 +4165,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MACOSX_DEPLOYMENT_TARGET = 11.0;
@@ -4185,7 +4185,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MACOSX_DEPLOYMENT_TARGET = 11.0;
@@ -4349,7 +4349,7 @@
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -4366,7 +4366,9 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = YES;
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;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -4401,7 +4403,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
@@ -4415,7 +4417,9 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UIRequiresFullScreen = YES;
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;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -4453,7 +4457,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEAD_CODE_STRIPPING = YES;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
@@ -4492,13 +4496,14 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEAD_CODE_STRIPPING = YES;
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly;
GCC_OPTIMIZATION_LEVEL = 1;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = macOS/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video";
@@ -4526,7 +4531,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4549,7 +4554,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4574,7 +4579,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEAD_CODE_STRIPPING = YES;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4598,7 +4603,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEAD_CODE_STRIPPING = YES;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4624,7 +4629,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_ASSET_PATHS = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -4664,7 +4669,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_ASSET_PATHS = "";
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
ENABLE_PREVIEWS = YES;
@@ -4704,7 +4709,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -4727,7 +4732,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View File

@@ -1,16 +1,17 @@
import AVFoundation
import Defaults
import Foundation
import Logging
import UIKit
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!
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
orientationLock
return orientationLock
}
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)
UIViewController.swizzleHomeIndicatorProperty()
OrientationTracker.shared.startDeviceOrientationTracking()
OrientationModel.shared.startOrientationUpdates()
// Configure the audio session for playback
do {

View File

@@ -1,10 +1,12 @@
import Defaults
import Foundation
import Logging
import Repeat
import SwiftUI
final class OrientationModel {
static var shared = OrientationModel()
let logger = Logger(label: "stream.yattee.orientation.model")
var orientation = UIInterfaceOrientation.portrait
var lastOrientation: UIInterfaceOrientation?
@@ -13,79 +15,69 @@ final class OrientationModel {
private var player = PlayerModel.shared
func configureOrientationUpdatesBasedOnAccelerometer() {
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
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)
}
}
func startOrientationUpdates() {
// Ensure the orientation observer is active
orientationObserver = NotificationCenter.default.addObserver(
forName: OrientationTracker.deviceOrientationChangedNotification,
object: nil,
queue: .main
) { _ in
guard !Defaults[.honorSystemOrientationLock],
self.player.presentingPlayer,
!self.player.playingInPictureInPicture,
self.player.lockedOrientation.isNil
self.logger.info("Notification received: Device orientation changed.")
// We only allow .portrait and are not showing the player
guard (!self.player.presentingPlayer && !Defaults[.lockPortraitWhenBrowsing]) || self.player.presentingPlayer
else {
return
}
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
if self.lastOrientation != orientation {
self.lastOrientation = orientation
self.logger.info("Orientation changed to: \(orientation)")
} else {
self.logger.info("Orientation has not changed.")
}
// 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
}
self.lastOrientation = orientation
DispatchQueue.main.async {
guard Defaults[.enterFullscreenInLandscape],
self.player.presentingPlayer
else {
return
}
self.orientationDebouncer.callback = {
DispatchQueue.main.async {
if orientation.isLandscape {
self.player.controls.presentingControls = false
self.player.enterFullScreen(showControls: false)
if Defaults[.enterFullscreenInLandscape], self.player.presentingPlayer {
self.logger.info("Entering fullscreen because orientation is landscape.")
self.player.controls.presentingControls = false
self.player.enterFullScreen(showControls: false)
}
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
} else {
self.player.exitFullScreen(showControls: false)
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
self.logger.info("Exiting fullscreen because orientation is portrait.")
if self.player.playingFullScreen {
self.player.exitFullScreen(showControls: false)
}
if Defaults[.lockPortraitWhenBrowsing] {
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
} else {
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
}
}
}
}
self.orientationDebouncer.call()
}
}
}
func stopOrientationUpdates() {
guard let observer = orientationObserver else { return }
NotificationCenter.default.removeObserver(observer)
}
func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
logger.info("Locking orientation to: \(orientation), rotating to: \(String(describing: rotateOrientation)).")
if let rotateOrientation {
self.orientation = rotateOrientation
lastOrientation = rotateOrientation