diff --git a/osu.Android/AndroidOrientationManager.cs b/osu.Android/AndroidOrientationManager.cs deleted file mode 100644 index 76d2fc24cb..0000000000 --- a/osu.Android/AndroidOrientationManager.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Android.Content.PM; -using Android.Content.Res; -using osu.Framework.Allocation; -using osu.Game.Mobile; - -namespace osu.Android -{ - public partial class AndroidOrientationManager : OrientationManager - { - [Resolved] - private OsuGameActivity gameActivity { get; set; } = null!; - - protected override bool IsCurrentOrientationPortrait => gameActivity.Resources!.Configuration!.Orientation == Orientation.Portrait; - protected override bool IsTablet => gameActivity.IsTablet; - - protected override void SetAllowedOrientations(GameOrientation? orientation) - => gameActivity.RequestedOrientation = orientation == null ? gameActivity.DefaultOrientation : toScreenOrientation(orientation.Value); - - private static ScreenOrientation toScreenOrientation(GameOrientation orientation) - { - if (orientation == GameOrientation.Locked) - return ScreenOrientation.Locked; - - if (orientation == GameOrientation.Portrait) - return ScreenOrientation.Portrait; - - if (orientation == GameOrientation.Landscape) - return ScreenOrientation.Landscape; - - if (orientation == GameOrientation.FullPortrait) - return ScreenOrientation.SensorPortrait; - - return ScreenOrientation.SensorLandscape; - } - } -} diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 4143c8cae6..0f2451f0a0 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -3,11 +3,13 @@ using System; using Android.App; +using Android.Content.PM; using Microsoft.Maui.Devices; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Game; +using osu.Game.Screens; using osu.Game.Updater; using osu.Game.Utils; @@ -71,7 +73,35 @@ namespace osu.Android protected override void LoadComplete() { base.LoadComplete(); - LoadComponentAsync(new AndroidOrientationManager(), Add); + UserPlayingState.BindValueChanged(_ => updateOrientation()); + } + + protected override void ScreenChanged(IOsuScreen? current, IOsuScreen? newScreen) + { + base.ScreenChanged(current, newScreen); + + if (newScreen != null) + updateOrientation(); + } + + private void updateOrientation() + { + var orientation = MobileUtils.GetOrientation(this, (IOsuScreen)ScreenStack.CurrentScreen, gameActivity.IsTablet); + + switch (orientation) + { + case MobileUtils.Orientation.Locked: + gameActivity.RequestedOrientation = ScreenOrientation.Locked; + break; + + case MobileUtils.Orientation.Portrait: + gameActivity.RequestedOrientation = ScreenOrientation.Portrait; + break; + + case MobileUtils.Orientation.Default: + gameActivity.RequestedOrientation = gameActivity.DefaultOrientation; + break; + } } public override void SetHost(GameHost host) diff --git a/osu.Game/Mobile/GameOrientation.cs b/osu.Game/Mobile/GameOrientation.cs deleted file mode 100644 index 0022c8fefb..0000000000 --- a/osu.Game/Mobile/GameOrientation.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Mobile -{ - public enum GameOrientation - { - /// - /// Lock the game orientation. - /// - Locked, - - /// - /// Display the game in regular portrait orientation. - /// - Portrait, - - /// - /// Display the game in landscape-right orientation. - /// - Landscape, - - /// - /// Display the game in landscape-right/landscape-left orientations. - /// - FullLandscape, - - /// - /// Display the game in portrait/portrait-upside-down orientations. - /// This is exclusive to tablet mobile devices. - /// - FullPortrait, - } -} diff --git a/osu.Game/Mobile/OrientationManager.cs b/osu.Game/Mobile/OrientationManager.cs deleted file mode 100644 index 964b40e2af..0000000000 --- a/osu.Game/Mobile/OrientationManager.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Game.Screens.Play; - -namespace osu.Game.Mobile -{ - /// - /// A that manages the device orientations a game can display in. - /// - public abstract partial class OrientationManager : Component - { - /// - /// Whether the current orientation of the game is portrait. - /// - protected abstract bool IsCurrentOrientationPortrait { get; } - - /// - /// Whether the mobile device is considered a tablet. - /// - protected abstract bool IsTablet { get; } - - [Resolved] - private OsuGame game { get; set; } = null!; - - [Resolved] - private ILocalUserPlayInfo localUserPlayInfo { get; set; } = null!; - - private IBindable requiresPortraitOrientation = null!; - private IBindable localUserPlaying = null!; - - protected override void LoadComplete() - { - base.LoadComplete(); - - requiresPortraitOrientation = game.RequiresPortraitOrientation.GetBoundCopy(); - requiresPortraitOrientation.BindValueChanged(_ => updateOrientations()); - - localUserPlaying = localUserPlayInfo.PlayingState.GetBoundCopy(); - localUserPlaying.BindValueChanged(_ => updateOrientations()); - - updateOrientations(); - } - - private void updateOrientations() - { - bool lockCurrentOrientation = localUserPlaying.Value == LocalUserPlayingState.Playing; - bool lockToPortraitOnPhone = requiresPortraitOrientation.Value; - - if (IsTablet) - { - if (lockCurrentOrientation) - SetAllowedOrientations(GameOrientation.Locked); - else - SetAllowedOrientations(null); - } - else - { - if (lockToPortraitOnPhone) - SetAllowedOrientations(GameOrientation.Portrait); - else if (lockCurrentOrientation) - SetAllowedOrientations(GameOrientation.Locked); - else - SetAllowedOrientations(null); - } - } - - /// - /// Sets the allowed orientations the device can rotate to. - /// - /// The allowed orientations, or null to return back to default. - protected abstract void SetAllowedOrientations(GameOrientation? orientation); - } -} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index cc6613da89..89aba818a3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -173,25 +173,14 @@ namespace osu.Game /// public readonly IBindable OverlayActivationMode = new Bindable(); - /// - /// On mobile phones, this specifies whether the device should be set and locked to portrait orientation. - /// Tablet devices are unaffected by this property. - /// - /// - /// Implementations can be viewed in mobile projects. - /// - public IBindable RequiresPortraitOrientation => requiresPortraitOrientation; - - private readonly Bindable requiresPortraitOrientation = new BindableBool(); - /// /// Whether the back button is currently displayed. /// private readonly IBindable backButtonVisibility = new Bindable(); - IBindable ILocalUserPlayInfo.PlayingState => playingState; + IBindable ILocalUserPlayInfo.PlayingState => UserPlayingState; - private readonly Bindable playingState = new Bindable(); + protected readonly Bindable UserPlayingState = new Bindable(); protected OsuScreenStack ScreenStack; @@ -319,7 +308,7 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() { var userInputManager = base.CreateUserInputManager(); - (userInputManager as OsuUserInputManager)?.PlayingState.BindTo(playingState); + (userInputManager as OsuUserInputManager)?.PlayingState.BindTo(UserPlayingState); return userInputManager; } @@ -414,7 +403,7 @@ namespace osu.Game // Transfer any runtime changes back to configuration file. SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString(); - playingState.BindValueChanged(p => + UserPlayingState.BindValueChanged(p => { BeatmapManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying; SkinManager.PauseImports = p.NewValue != LocalUserPlayingState.NotPlaying; @@ -1555,7 +1544,7 @@ namespace osu.Game GlobalCursorDisplay.ShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } - private void screenChanged(IScreen current, IScreen newScreen) + protected virtual void ScreenChanged([CanBeNull] IOsuScreen current, [CanBeNull] IOsuScreen newScreen) { SentrySdk.ConfigureScope(scope => { @@ -1571,10 +1560,10 @@ namespace osu.Game switch (current) { case Player player: - player.PlayingState.UnbindFrom(playingState); + player.PlayingState.UnbindFrom(UserPlayingState); // reset for sanity. - playingState.Value = LocalUserPlayingState.NotPlaying; + UserPlayingState.Value = LocalUserPlayingState.NotPlaying; break; } @@ -1591,7 +1580,7 @@ namespace osu.Game break; case Player player: - player.PlayingState.BindTo(playingState); + player.PlayingState.BindTo(UserPlayingState); break; default: @@ -1599,32 +1588,32 @@ namespace osu.Game break; } - if (current is IOsuScreen currentOsuScreen) + if (current != null) { - backButtonVisibility.UnbindFrom(currentOsuScreen.BackButtonVisibility); - OverlayActivationMode.UnbindFrom(currentOsuScreen.OverlayActivationMode); - configUserActivity.UnbindFrom(currentOsuScreen.Activity); + backButtonVisibility.UnbindFrom(current.BackButtonVisibility); + OverlayActivationMode.UnbindFrom(current.OverlayActivationMode); + configUserActivity.UnbindFrom(current.Activity); } - if (newScreen is IOsuScreen newOsuScreen) + // Bind to new screen. + if (newScreen != null) { - backButtonVisibility.BindTo(newOsuScreen.BackButtonVisibility); - OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode); - configUserActivity.BindTo(newOsuScreen.Activity); + backButtonVisibility.BindTo(newScreen.BackButtonVisibility); + OverlayActivationMode.BindTo(newScreen.OverlayActivationMode); + configUserActivity.BindTo(newScreen.Activity); - GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput; + // Handle various configuration updates based on new screen settings. + GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newScreen.HideMenuCursorOnNonMouseInput; - requiresPortraitOrientation.Value = newOsuScreen.RequiresPortraitOrientation; - - if (newOsuScreen.HideOverlaysOnEnter) + if (newScreen.HideOverlaysOnEnter) CloseAllOverlays(); else Toolbar.Show(); - if (newOsuScreen.ShowFooter) + if (newScreen.ShowFooter) { BackButton.Hide(); - ScreenFooter.SetButtons(newOsuScreen.CreateFooterButtons()); + ScreenFooter.SetButtons(newScreen.CreateFooterButtons()); ScreenFooter.Show(); } else @@ -1632,16 +1621,16 @@ namespace osu.Game ScreenFooter.SetButtons(Array.Empty()); ScreenFooter.Hide(); } - } - skinEditor.SetTarget((OsuScreen)newScreen); + skinEditor.SetTarget((OsuScreen)newScreen); + } } - private void screenPushed(IScreen lastScreen, IScreen newScreen) => screenChanged(lastScreen, newScreen); + private void screenPushed(IScreen lastScreen, IScreen newScreen) => ScreenChanged((OsuScreen)lastScreen, (OsuScreen)newScreen); private void screenExited(IScreen lastScreen, IScreen newScreen) { - screenChanged(lastScreen, newScreen); + ScreenChanged((OsuScreen)lastScreen, (OsuScreen)newScreen); if (newScreen == null) Exit(); diff --git a/osu.Game/Utils/MobileUtils.cs b/osu.Game/Utils/MobileUtils.cs new file mode 100644 index 0000000000..6e59efb71c --- /dev/null +++ b/osu.Game/Utils/MobileUtils.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Screens; +using osu.Game.Screens.Play; + +namespace osu.Game.Utils +{ + public static class MobileUtils + { + /// + /// Determines the correct state which a mobile device should be put into for the given information. + /// + /// Information about whether the user is currently playing. + /// The current screen which the user is at. + /// Whether the user is playing on a mobile tablet device instead of a phone. + public static Orientation GetOrientation(ILocalUserPlayInfo userPlayInfo, IOsuScreen currentScreen, bool isTablet) + { + bool lockCurrentOrientation = userPlayInfo.PlayingState.Value == LocalUserPlayingState.Playing; + bool lockToPortraitOnPhone = currentScreen.RequiresPortraitOrientation; + + if (lockToPortraitOnPhone && !isTablet) + return Orientation.Portrait; + + if (lockCurrentOrientation) + return Orientation.Locked; + + return Orientation.Default; + } + + public enum Orientation + { + /// + /// Lock the game orientation. + /// + Locked, + + /// + /// Lock the game to portrait orientation (does not include upside-down portrait). + /// + Portrait, + + /// + /// Use the application's default settings. + /// + Default, + } + } +} diff --git a/osu.iOS/IOSOrientationManager.cs b/osu.iOS/IOSOrientationManager.cs deleted file mode 100644 index 6d5bb990c2..0000000000 --- a/osu.iOS/IOSOrientationManager.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Mobile; -using UIKit; - -namespace osu.iOS -{ - public partial class IOSOrientationManager : OrientationManager - { - private readonly AppDelegate appDelegate; - - protected override bool IsCurrentOrientationPortrait => appDelegate.CurrentOrientation.IsPortrait(); - protected override bool IsTablet => UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad; - - public IOSOrientationManager(AppDelegate appDelegate) - { - this.appDelegate = appDelegate; - } - - protected override void SetAllowedOrientations(GameOrientation? orientation) - => appDelegate.Orientations = orientation == null ? null : toUIInterfaceOrientationMask(orientation.Value); - - private UIInterfaceOrientationMask toUIInterfaceOrientationMask(GameOrientation orientation) - { - if (orientation == GameOrientation.Locked) - return (UIInterfaceOrientationMask)(1 << (int)appDelegate.CurrentOrientation); - - if (orientation == GameOrientation.Portrait) - return UIInterfaceOrientationMask.Portrait; - - if (orientation == GameOrientation.Landscape) - return UIInterfaceOrientationMask.LandscapeRight; - - if (orientation == GameOrientation.FullPortrait) - return UIInterfaceOrientationMask.Portrait | UIInterfaceOrientationMask.PortraitUpsideDown; - - return UIInterfaceOrientationMask.Landscape; - } - } -} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index ed47a1e8b8..a5a42c1e66 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -8,8 +8,10 @@ using osu.Framework.Graphics; using osu.Framework.iOS; using osu.Framework.Platform; using osu.Game; +using osu.Game.Screens; using osu.Game.Updater; using osu.Game.Utils; +using UIKit; namespace osu.iOS { @@ -28,7 +30,36 @@ namespace osu.iOS protected override void LoadComplete() { base.LoadComplete(); - LoadComponentAsync(new IOSOrientationManager(appDelegate), Add); + UserPlayingState.BindValueChanged(_ => updateOrientation()); + } + + protected override void ScreenChanged(IOsuScreen? current, IOsuScreen? newScreen) + { + base.ScreenChanged(current, newScreen); + + if (newScreen != null) + updateOrientation(); + } + + private void updateOrientation() + { + bool iPad = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad; + var orientation = MobileUtils.GetOrientation(this, (IOsuScreen)ScreenStack.CurrentScreen, iPad); + + switch (orientation) + { + case MobileUtils.Orientation.Locked: + appDelegate.Orientations = (UIInterfaceOrientationMask)(1 << (int)appDelegate.CurrentOrientation); + break; + + case MobileUtils.Orientation.Portrait: + appDelegate.Orientations = UIInterfaceOrientationMask.Portrait; + break; + + case MobileUtils.Orientation.Default: + appDelegate.Orientations = null; + break; + } } protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();