1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 10:43:22 +08:00

Abstractify orientation handling and add Android support

This commit is contained in:
Salman Alshamrani 2024-12-30 15:04:21 -05:00
parent d7e4038f4a
commit 0cd7f1b2d4
9 changed files with 204 additions and 114 deletions

View File

@ -0,0 +1,39 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
}
}
}

View File

@ -1,34 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Android.Content.PM;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Screens.Play;
namespace osu.Android
{
public partial class GameplayScreenRotationLocker : Component
{
private IBindable<LocalUserPlayingState> localUserPlaying = null!;
[Resolved]
private OsuGameActivity gameActivity { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(ILocalUserPlayInfo localUserPlayInfo)
{
localUserPlaying = localUserPlayInfo.PlayingState.GetBoundCopy();
localUserPlaying.BindValueChanged(updateLock, true);
}
private void updateLock(ValueChangedEvent<LocalUserPlayingState> userPlaying)
{
gameActivity.RunOnUiThread(() =>
{
gameActivity.RequestedOrientation = userPlaying.NewValue == LocalUserPlayingState.Playing ? ScreenOrientation.Locked : gameActivity.DefaultOrientation;
});
}
}
}

View File

@ -50,6 +50,8 @@ namespace osu.Android
/// <remarks>Adjusted on startup to match expected UX for the current device type (phone/tablet).</remarks>
public ScreenOrientation DefaultOrientation = ScreenOrientation.Unspecified;
public bool IsTablet { get; private set; }
private OsuGameAndroid game = null!;
protected override Framework.Game CreateGame() => game = new OsuGameAndroid(this);
@ -76,9 +78,9 @@ namespace osu.Android
WindowManager.DefaultDisplay.GetSize(displaySize);
#pragma warning restore CA1422
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
bool isTablet = smallestWidthDp >= 600f;
IsTablet = smallestWidthDp >= 600f;
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
RequestedOrientation = DefaultOrientation = IsTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
// Currently (SDK 6.0.200), BundleAssemblies is not runnable for net6-android.
// The assembly files are not available as files either after native AOT.

View File

@ -71,7 +71,7 @@ namespace osu.Android
protected override void LoadComplete()
{
base.LoadComplete();
LoadComponentAsync(new GameplayScreenRotationLocker(), Add);
LoadComponentAsync(new AndroidOrientationManager(), Add);
}
public override void SetHost(GameHost host)

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Mobile
{
public enum GameOrientation
{
/// <summary>
/// Lock the game orientation.
/// </summary>
Locked,
/// <summary>
/// Display the game in regular portrait orientation.
/// </summary>
Portrait,
/// <summary>
/// Display the game in landscape-right orientation.
/// </summary>
Landscape,
/// <summary>
/// Display the game in landscape-right/landscape-left orientations.
/// </summary>
FullLandscape,
/// <summary>
/// Display the game in portrait/portrait-upside-down orientations.
/// This is exclusive to tablet mobile devices.
/// </summary>
FullPortrait,
}
}

View File

@ -0,0 +1,84 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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
{
/// <summary>
/// A <see cref="Component"/> that manages the device orientations a game can display in.
/// </summary>
public abstract partial class OrientationManager : Component
{
/// <summary>
/// Whether the current orientation of the game is portrait.
/// </summary>
protected abstract bool IsCurrentOrientationPortrait { get; }
/// <summary>
/// Whether the mobile device is considered a tablet.
/// </summary>
protected abstract bool IsTablet { get; }
[Resolved]
private OsuGame game { get; set; } = null!;
[Resolved]
private ILocalUserPlayInfo localUserPlayInfo { get; set; } = null!;
private IBindable<bool> requiresPortraitOrientation = null!;
private IBindable<LocalUserPlayingState> 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 lockToPortrait = requiresPortraitOrientation.Value;
if (lockCurrentOrientation)
{
if (lockToPortrait && !IsCurrentOrientationPortrait)
SetAllowedOrientations(GameOrientation.Portrait);
else if (!lockToPortrait && IsCurrentOrientationPortrait && !IsTablet)
SetAllowedOrientations(GameOrientation.Landscape);
else
SetAllowedOrientations(GameOrientation.Locked);
return;
}
if (lockToPortrait)
{
if (IsTablet)
SetAllowedOrientations(GameOrientation.FullPortrait);
else
SetAllowedOrientations(GameOrientation.Portrait);
return;
}
SetAllowedOrientations(null);
}
/// <summary>
/// Sets the allowed orientations the device can rotate to.
/// </summary>
/// <param name="orientation">The allowed orientations, or null to return back to default.</param>
protected abstract void SetAllowedOrientations(GameOrientation? orientation);
}
}

View File

@ -1,76 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
using osu.Game.Screens.Play;
using UIKit;
namespace osu.iOS
{
public partial class IOSOrientationHandler : Component
{
private readonly AppDelegate appDelegate;
[Resolved]
private OsuGame game { get; set; } = null!;
[Resolved]
private ILocalUserPlayInfo localUserPlayInfo { get; set; } = null!;
private IBindable<bool> requiresPortraitOrientation = null!;
private IBindable<LocalUserPlayingState> localUserPlaying = null!;
public IOSOrientationHandler(AppDelegate appDelegate)
{
this.appDelegate = appDelegate;
}
protected override void LoadComplete()
{
base.LoadComplete();
requiresPortraitOrientation = game.RequiresPortraitOrientation.GetBoundCopy();
requiresPortraitOrientation.BindValueChanged(_ => updateOrientations());
localUserPlaying = localUserPlayInfo.PlayingState.GetBoundCopy();
localUserPlaying.BindValueChanged(_ => updateOrientations());
updateOrientations();
}
private void updateOrientations()
{
UIInterfaceOrientation currentOrientation = appDelegate.CurrentOrientation;
bool lockCurrentOrientation = localUserPlaying.Value == LocalUserPlayingState.Playing;
bool lockToPortrait = requiresPortraitOrientation.Value;
bool isPhone = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone;
if (lockCurrentOrientation)
{
if (lockToPortrait && !currentOrientation.IsPortrait())
currentOrientation = UIInterfaceOrientation.Portrait;
else if (!lockToPortrait && currentOrientation.IsPortrait() && isPhone)
currentOrientation = UIInterfaceOrientation.LandscapeRight;
appDelegate.Orientations = (UIInterfaceOrientationMask)(1 << (int)currentOrientation);
return;
}
if (lockToPortrait)
{
UIInterfaceOrientationMask portraitOrientations = UIInterfaceOrientationMask.Portrait;
if (!isPhone)
portraitOrientations |= UIInterfaceOrientationMask.PortraitUpsideDown;
appDelegate.Orientations = portraitOrientations;
return;
}
appDelegate.Orientations = null;
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
}
}
}

View File

@ -28,7 +28,7 @@ namespace osu.iOS
protected override void LoadComplete()
{
base.LoadComplete();
Add(new IOSOrientationHandler(appDelegate));
LoadComponentAsync(new IOSOrientationManager(appDelegate), Add);
}
protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();