From d6e75295e1d1b9cb9de73c83ba8ec81f87d21286 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sun, 2 Jan 2022 20:41:04 +0100 Subject: [PATCH] Add iOS option to lock portrait mode in browsing --- Shared/Defaults.swift | 6 +++++ Shared/Navigation/ContentView.swift | 6 +++++ Shared/Settings/BrowsingSettings.swift | 13 +++++++++++ Shared/YatteeApp.swift | 2 ++ Yattee.xcodeproj/project.pbxproj | 14 ++++++++++-- iOS/AppDelegate.swift | 17 ++++++++++++++ iOS/Orientation.swift | 31 ++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 iOS/AppDelegate.swift create mode 100644 iOS/Orientation.swift diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index cbaa7a51..a38feb9e 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -1,5 +1,8 @@ import Defaults import Foundation +#if os(iOS) + import UIKit +#endif extension Defaults.Keys { static let kavinPipedInstanceID = "kavin-piped" @@ -34,6 +37,9 @@ extension Defaults.Keys { #if !os(tvOS) static let accountPickerDisplaysUsername = Key("accountPickerDisplaysUsername", default: false) #endif + #if os(iOS) + static let lockPortraitWhenBrowsing = Key("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone) + #endif static let channelOnThumbnail = Key("channelOnThumbnail", default: true) static let timeOnThumbnail = Key("timeOnThumbnail", default: true) static let showHistoryInPlayer = Key("showHistoryInPlayer", default: false) diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index e4b0ffb3..19649272 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -109,6 +109,12 @@ struct ContentView: View { try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback) #endif + #if os(iOS) + if Defaults[.lockPortraitWhenBrowsing] { + Orientation.lockOrientation(.portrait, andRotateTo: .portrait) + } + #endif + if let account = accounts.lastUsed ?? instances.lastUsed?.anonymousAccount ?? InstancesModel.all.first?.anonymousAccount diff --git a/Shared/Settings/BrowsingSettings.swift b/Shared/Settings/BrowsingSettings.swift index 11fc3898..413b4623 100644 --- a/Shared/Settings/BrowsingSettings.swift +++ b/Shared/Settings/BrowsingSettings.swift @@ -5,6 +5,9 @@ struct BrowsingSettings: View { #if !os(tvOS) @Default(.accountPickerDisplaysUsername) private var accountPickerDisplaysUsername #endif + #if os(iOS) + @Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing + #endif @Default(.channelOnThumbnail) private var channelOnThumbnail @Default(.timeOnThumbnail) private var timeOnThumbnail @Default(.visibleSections) private var visibleSections @@ -15,6 +18,16 @@ struct BrowsingSettings: View { #if !os(tvOS) Toggle("Show username in the account picker button", isOn: $accountPickerDisplaysUsername) #endif + #if os(iOS) + Toggle("Lock portrait mode", isOn: $lockPortraitWhenBrowsing) + .onChange(of: lockPortraitWhenBrowsing) { lock in + if lock { + Orientation.lockOrientation(.portrait, andRotateTo: .portrait) + } else { + Orientation.lockOrientation(.allButUpsideDown) + } + } + #endif Toggle("Show channel name on thumbnail", isOn: $channelOnThumbnail) Toggle("Show video length on thumbnail", isOn: $timeOnThumbnail) } diff --git a/Shared/YatteeApp.swift b/Shared/YatteeApp.swift index 007474a4..08727a8f 100644 --- a/Shared/YatteeApp.swift +++ b/Shared/YatteeApp.swift @@ -6,6 +6,8 @@ struct YatteeApp: App { #if os(macOS) @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var updater = UpdaterModel() + #elseif os(iOS) + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate #endif @StateObject private var accounts = AccountsModel() diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 50d570b7..cce71d2c 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -367,6 +367,8 @@ 37B2631A2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B263192735EAAB00FE0D40 /* FavoriteResourceObserver.swift */; }; 37B2631B2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B263192735EAAB00FE0D40 /* FavoriteResourceObserver.swift */; }; 37B2631C2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B263192735EAAB00FE0D40 /* FavoriteResourceObserver.swift */; }; + 37B4E803277D0A72004BF56A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B4E802277D0A72004BF56A /* AppDelegate.swift */; }; + 37B4E805277D0AB4004BF56A /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B4E804277D0AB4004BF56A /* Orientation.swift */; }; 37B767DB2677C3CA0098BAA8 /* PlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */; }; 37B767DC2677C3CA0098BAA8 /* PlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */; }; 37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */; }; @@ -699,6 +701,8 @@ 37B044B626F7AB9000E1419D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoContextMenuView.swift; sourceTree = ""; }; 37B263192735EAAB00FE0D40 /* FavoriteResourceObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteResourceObserver.swift; sourceTree = ""; }; + 37B4E802277D0A72004BF56A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 37B4E804277D0AB4004BF56A /* Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Orientation.swift; sourceTree = ""; }; 37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerModel.swift; sourceTree = ""; }; 37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLHandler.swift; sourceTree = ""; }; 37B81AF826D2C9A700675966 /* VideoPlayerSizeModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSizeModifier.swift; sourceTree = ""; }; @@ -1101,6 +1105,8 @@ 37992DC826CC50CD003D4C27 /* iOS */ = { isa = PBXGroup; children = ( + 37B4E802277D0A72004BF56A /* AppDelegate.swift */, + 37B4E804277D0AB4004BF56A /* Orientation.swift */, 3784B23A272894DA00B09468 /* ShareSheet.swift */, 37992DC726CC50BC003D4C27 /* Info.plist */, ); @@ -1848,6 +1854,7 @@ 37B81AF926D2C9A700675966 /* VideoPlayerSizeModifier.swift in Sources */, 37C0698227260B2100F7F6CB /* ThumbnailsModel.swift in Sources */, 37BC50A82778A84700510953 /* HistorySettings.swift in Sources */, + 37B4E805277D0AB4004BF56A /* Orientation.swift in Sources */, 37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */, 371B7E662759786B00D21217 /* Comment+Fixtures.swift in Sources */, 37BE0BD326A1D4780092E2DB /* Player.swift in Sources */, @@ -1872,6 +1879,7 @@ 375168D62700FAFF008F96A6 /* Debounce.swift in Sources */, 37E64DD126D597EB00C71877 /* SubscriptionsModel.swift in Sources */, 376578892685471400D4EA09 /* Playlist.swift in Sources */, + 37B4E803277D0A72004BF56A /* AppDelegate.swift in Sources */, 373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */, 3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */, 3782B9522755667600990149 /* String+Format.swift in Sources */, @@ -2626,7 +2634,8 @@ INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UIRequiresFullScreen = NO; + INFOPLIST_KEY_UIRequiresFullScreen = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -2657,7 +2666,8 @@ INFOPLIST_FILE = iOS/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UIRequiresFullScreen = NO; + INFOPLIST_KEY_UIRequiresFullScreen = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift new file mode 100644 index 00000000..b4d5f793 --- /dev/null +++ b/iOS/AppDelegate.swift @@ -0,0 +1,17 @@ +import Foundation +import UIKit + +final class AppDelegate: UIResponder, UIApplicationDelegate { + var orientationLock = UIInterfaceOrientationMask.all + + private(set) static var instance: AppDelegate! + + func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask { + orientationLock + } + + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection + AppDelegate.instance = self + return true + } +} diff --git a/iOS/Orientation.swift b/iOS/Orientation.swift new file mode 100644 index 00000000..6fa2badc --- /dev/null +++ b/iOS/Orientation.swift @@ -0,0 +1,31 @@ +import CoreMotion +import Defaults +import Logging +import UIKit + +struct Orientation { + static var logger = Logger(label: "stream.yattee.orientation") + + static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { + if let delegate = AppDelegate.instance { + delegate.orientationLock = orientation + + let orientationString = orientation == .portrait ? "portrait" : orientation == .landscapeLeft ? "landscapeLeft" : + orientation == .landscapeRight ? "landscapeRight" : orientation == .portraitUpsideDown ? "portraitUpsideDown" : + orientation == .landscape ? "landscape" : orientation == .all ? "all" : "allButUpsideDown" + + logger.info("locking \(orientationString)") + } + } + + static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) { + lockOrientation(orientation) + + guard !rotateOrientation.isNil else { + return + } + + UIDevice.current.setValue(rotateOrientation!.rawValue, forKey: "orientation") + UINavigationController.attemptRotationToDeviceOrientation() + } +}