diff --git a/Platforms/Game/.Android/AndroidConfigChangedEventArgs.cs b/Platforms/Game/.Android/AndroidConfigChangedEventArgs.cs new file mode 100644 index 00000000000..aa6a547e666 --- /dev/null +++ b/Platforms/Game/.Android/AndroidConfigChangedEventArgs.cs @@ -0,0 +1,30 @@ +// Copyright (C)2025 Nick Kastellanos + +using System; +using Microsoft.Xna.Framework; + + +namespace Microsoft.Xna.Platform +{ + internal class AndroidConfigChangedEventArgs : EventArgs + { + public readonly Android.Content.Res.Configuration NewConfig; + + public AndroidConfigChangedEventArgs(Android.Content.Res.Configuration newConfig) + { + NewConfig = newConfig; + } + } + + internal class AndroidConfigChangedOrientationEventArgs : AndroidConfigChangedEventArgs + { + public readonly Android.Content.Res.Orientation NewOrientation; + + public AndroidConfigChangedOrientationEventArgs(Android.Content.Res.Configuration newConfig, Android.Content.Res.Orientation newOrientation) + : base(newConfig) + { + this.NewOrientation = newOrientation; + } + } + +} diff --git a/Platforms/Game/.Android/AndroidGameActivity.cs b/Platforms/Game/.Android/AndroidGameActivity.cs index 87aef83b438..185aa1bdaf6 100644 --- a/Platforms/Game/.Android/AndroidGameActivity.cs +++ b/Platforms/Game/.Android/AndroidGameActivity.cs @@ -20,6 +20,7 @@ public class AndroidGameActivity : Activity internal event EventHandler WindowFocused; internal event EventHandler WindowUnfocused; + internal event EventHandler WindowOrientationChanged; public event EventHandler Paused; public event EventHandler Resumed; @@ -49,8 +50,18 @@ protected override void OnCreate(Bundle savedInstanceState) public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig) { - // we need to refresh the viewport here. base.OnConfigurationChanged(newConfig); + + Android.Content.PM.ConfigChanges changes = (Android.Content.PM.ConfigChanges)newConfig.UpdateFrom(Resources.Configuration); + + if ((changes & Android.Content.PM.ConfigChanges.Orientation) != 0) + { + Android.Content.Res.Orientation newOrientation = newConfig.Orientation; + + var handler = WindowOrientationChanged; + if (handler != null) + handler(this, new AndroidConfigChangedOrientationEventArgs(newConfig, newOrientation)); + } } protected override void OnPause() diff --git a/Platforms/Game/.Android/AndroidGameWindow.cs b/Platforms/Game/.Android/AndroidGameWindow.cs index fab677b2dd6..547372b21c7 100644 --- a/Platforms/Game/.Android/AndroidGameWindow.cs +++ b/Platforms/Game/.Android/AndroidGameWindow.cs @@ -44,13 +44,12 @@ internal static AndroidGameWindow FromHandle(IntPtr windowHandle) internal RunnableObject _runner; internal AndroidGameActivity _activity; - private readonly Game _game; + internal readonly Game _game; private bool _isActivated = false; private AndroidGameWindow.AppState _appState = AndroidGameWindow.AppState.Exited; MediaState _mediaPlayer_PrevState = MediaState.Stopped; private Rectangle _clientBounds; - internal DisplayOrientation _supportedOrientations = DisplayOrientation.Default; private DisplayOrientation _currentOrientation; private OrientationListener _orientationListener; @@ -70,6 +69,7 @@ public AndroidGameWindow(AndroidGameActivity activity, Game game) _activity.WindowFocused += Activity_WindowFocused; _activity.WindowUnfocused += Activity_WindowUnfocused; + _activity.WindowOrientationChanged += Activity_WindowOrientationChanged; Point size; // GetRealSize() was defined in JellyBeanMr1 / API 17 / Android 4.2 @@ -236,6 +236,12 @@ private void Activity_WindowUnfocused(object sender, EventArgs e) } } + private void Activity_WindowOrientationChanged(object sender, AndroidConfigChangedOrientationEventArgs e) + { + Android.Content.Res.Orientation NewOrientation = e.NewOrientation; + + } + internal void ForceSetFullScreen(bool _isFullScreen) { if (_isFullScreen) @@ -249,40 +255,13 @@ internal void ForceSetFullScreen(bool _isFullScreen) } } - /// - /// In Xna, setting SupportedOrientations = DisplayOrientation.Default (which is the default value) - /// has the effect of setting SupportedOrientations to landscape only or portrait only, based on the - /// aspect ratio of PreferredBackBufferWidth / PreferredBackBufferHeight - /// - /// - internal DisplayOrientation GetEffectiveSupportedOrientations() - { - if (_supportedOrientations == DisplayOrientation.Default) - { - GraphicsDeviceManager deviceManager = (_game.Services.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager); - if (deviceManager != null) - { - if (deviceManager.PreferredBackBufferWidth > deviceManager.PreferredBackBufferHeight) - return (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); - else - return (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); - } - - return (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); - } - - return _supportedOrientations; - } - /// /// Updates the screen orientation. Filters out requests for unsupported orientations. /// - internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphicsChanges) + internal void SetOrientation(DisplayOrientation newOrientation, DisplayOrientation supported, bool applyGraphicsChanges) { - DisplayOrientation supported = GetEffectiveSupportedOrientations(); - // If the new orientation is not supported, force a supported orientation - if ((supported & newOrientation) == 0) + if ((newOrientation & supported) == 0) { if ((supported & DisplayOrientation.LandscapeLeft) != 0) newOrientation = DisplayOrientation.LandscapeLeft; @@ -306,7 +285,7 @@ internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphi newOrientation = DisplayOrientation.PortraitDown; } - if ((supported & newOrientation) != 0) + if ((newOrientation & supported) != 0) { DisplayOrientation oldOrientation = CurrentOrientation; _currentOrientation = newOrientation; @@ -316,7 +295,7 @@ internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphi TouchPanel.DisplayOrientation = newOrientation; - if (applyGraphicsChanges && (oldOrientation != newOrientation)) + if ((newOrientation != oldOrientation) && applyGraphicsChanges) { GraphicsDeviceManager gdm = ((IPlatformGame)_game).GetStrategy().GraphicsDeviceManager; if (gdm != null) @@ -424,6 +403,7 @@ public void Dispose() _activity.WindowFocused += Activity_WindowFocused; _activity.WindowUnfocused += Activity_WindowUnfocused; + _activity.WindowOrientationChanged += Activity_WindowOrientationChanged; _activity = null; } diff --git a/Platforms/Game/.Android/ConcreteGraphicsDeviceManager.cs b/Platforms/Game/.Android/ConcreteGraphicsDeviceManager.cs index df4bbf676f5..a22dc3e49f8 100644 --- a/Platforms/Game/.Android/ConcreteGraphicsDeviceManager.cs +++ b/Platforms/Game/.Android/ConcreteGraphicsDeviceManager.cs @@ -66,9 +66,6 @@ public override DisplayOrientation SupportedOrientations set { base.SupportedOrientations = value; - - if (base.Game.Window != null) - ((AndroidGameWindow)base.Game.Window)._supportedOrientations = base.SupportedOrientations; } } @@ -111,7 +108,15 @@ public override void CreateDevice() // ApplyChanges { // Trigger a change in orientation in case the supported orientations have changed - androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, false); + DisplayOrientation supported = this.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, supported, false); base.GraphicsDevice.PresentationParameters.DisplayOrientation = base.Game.Window.CurrentOrientation; @@ -136,7 +141,15 @@ public override void CreateDevice() Android.App.Activity activity = AndroidGameWindow.Activity; DisplayOrientation currentOrientation = AndroidCompatibility.Current.GetAbsoluteOrientation(activity); - androidGameWindow.SetOrientation(currentOrientation, false); + DisplayOrientation supported2 = this.SupportedOrientations; + if (supported2 == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported2 = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported2 = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(currentOrientation, supported2, false); // TODO: check if the PreferredBackBufferWidth/Hight is supported and throw an error similar to fullscreen Windows Desktop. base.GraphicsDevice.PresentationParameters.BackBufferWidth = surfaceView.Width; @@ -165,7 +178,15 @@ public override void ApplyChanges() View surfaceView = androidGameWindow.GameView; // Trigger a change in orientation in case the supported orientations have changed - androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, false); + DisplayOrientation supported = this.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, supported, false); base.GraphicsDevice.PresentationParameters.DisplayOrientation = base.Game.Window.CurrentOrientation; diff --git a/Platforms/Game/.Android/OrientationListener.cs b/Platforms/Game/.Android/OrientationListener.cs index e8d2b17ef94..d30ca3e149a 100644 --- a/Platforms/Game/.Android/OrientationListener.cs +++ b/Platforms/Game/.Android/OrientationListener.cs @@ -46,8 +46,38 @@ public override void OnOrientationChanged(int orientation) DisplayOrientation absOrientation = AndroidCompatibility.Current.GetAbsoluteOrientation(orientation); - if ((_gameWindow.GetEffectiveSupportedOrientations() & absOrientation) == 0 - || absOrientation == _gameWindow.CurrentOrientation + GraphicsDeviceManager deviceManager = (_gameWindow._game.Services.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager); + if (deviceManager != null) + { + DisplayOrientation supported = deviceManager.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (deviceManager.PreferredBackBufferWidth <= deviceManager.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + + if ((supported & absOrientation) == 0) + { + targetOrientation = DisplayOrientation.Unknown; + elapsed = TimeSpan.Zero; + return; + } + } + else + { + DisplayOrientation supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + + if ((supported & absOrientation) == 0) + { + targetOrientation = DisplayOrientation.Unknown; + elapsed = TimeSpan.Zero; + return; + } + } + + if (absOrientation == _gameWindow.CurrentOrientation || absOrientation == DisplayOrientation.Unknown ) { @@ -86,9 +116,30 @@ internal void Update() // orientation must be stable for 0.5 seconds before changing. if (elapsed.TotalSeconds > 0.5) { - _gameWindow.SetOrientation(targetOrientation, true); - targetOrientation = DisplayOrientation.Unknown; - elapsed = TimeSpan.Zero; + GraphicsDeviceManager deviceManager = (_gameWindow._game.Services.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager); + if (deviceManager != null) + { + DisplayOrientation supported = deviceManager.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (deviceManager.PreferredBackBufferWidth <= deviceManager.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + _gameWindow.SetOrientation(targetOrientation, supported, true); + + targetOrientation = DisplayOrientation.Unknown; + elapsed = TimeSpan.Zero; + } + else + { + DisplayOrientation supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + _gameWindow.SetOrientation(targetOrientation, supported, true); + + targetOrientation = DisplayOrientation.Unknown; + elapsed = TimeSpan.Zero; + } } } } diff --git a/Platforms/Game/.CardboardLegacy/AndroidGameActivity.cs b/Platforms/Game/.CardboardLegacy/AndroidGameActivity.cs index e953b638b2f..81e4f285d77 100644 --- a/Platforms/Game/.CardboardLegacy/AndroidGameActivity.cs +++ b/Platforms/Game/.CardboardLegacy/AndroidGameActivity.cs @@ -21,6 +21,7 @@ public class AndroidGameActivity : VRCardboard.CardboardActivity internal event EventHandler WindowFocused; internal event EventHandler WindowUnfocused; + internal event EventHandler WindowOrientationChanged; public event EventHandler Paused; public event EventHandler Resumed; @@ -57,8 +58,18 @@ protected override void OnCreate(Bundle savedInstanceState) public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig) { - // we need to refresh the viewport here. base.OnConfigurationChanged(newConfig); + + Android.Content.PM.ConfigChanges changes = (Android.Content.PM.ConfigChanges)newConfig.UpdateFrom(Resources.Configuration); + + if ((changes & Android.Content.PM.ConfigChanges.Orientation) != 0) + { + Android.Content.Res.Orientation newOrientation = newConfig.Orientation; + + var handler = WindowOrientationChanged; + if (handler != null) + handler(this, new AndroidConfigChangedOrientationEventArgs(newConfig, newOrientation)); + } } protected override void OnPause() diff --git a/Platforms/Game/.CardboardLegacy/AndroidGameWindow.cs b/Platforms/Game/.CardboardLegacy/AndroidGameWindow.cs index 5821253c95b..1bcf9cd70b0 100644 --- a/Platforms/Game/.CardboardLegacy/AndroidGameWindow.cs +++ b/Platforms/Game/.CardboardLegacy/AndroidGameWindow.cs @@ -48,13 +48,12 @@ internal static AndroidGameWindow FromHandle(IntPtr windowHandle) internal AndroidSurfaceView GameView { get; private set; } internal AndroidGameActivity _activity; - private readonly Game _game; + internal readonly Game _game; private bool _isActivated = false; private AndroidGameWindow.AppState _appState = AndroidGameWindow.AppState.Exited; MediaState _mediaPlayer_PrevState = MediaState.Stopped; private Rectangle _clientBounds; - internal DisplayOrientation _supportedOrientations = DisplayOrientation.Default; private DisplayOrientation _currentOrientation; private OrientationListener _orientationListener; @@ -74,6 +73,7 @@ public AndroidGameWindow(AndroidGameActivity activity, Game game) _activity.WindowFocused += Activity_WindowFocused; _activity.WindowUnfocused += Activity_WindowUnfocused; + _activity.WindowOrientationChanged += Activity_WindowOrientationChanged; Point size; // GetRealSize() was defined in JellyBeanMr1 / API 17 / Android 4.2 @@ -236,6 +236,12 @@ private void Activity_WindowUnfocused(object sender, EventArgs e) } } + private void Activity_WindowOrientationChanged(object sender, AndroidConfigChangedOrientationEventArgs e) + { + Android.Content.Res.Orientation NewOrientation = e.NewOrientation; + + } + internal void ForceSetFullScreen(bool _isFullScreen) { if (_isFullScreen) @@ -249,40 +255,13 @@ internal void ForceSetFullScreen(bool _isFullScreen) } } - /// - /// In Xna, setting SupportedOrientations = DisplayOrientation.Default (which is the default value) - /// has the effect of setting SupportedOrientations to landscape only or portrait only, based on the - /// aspect ratio of PreferredBackBufferWidth / PreferredBackBufferHeight - /// - /// - internal DisplayOrientation GetEffectiveSupportedOrientations() - { - if (_supportedOrientations == DisplayOrientation.Default) - { - GraphicsDeviceManager deviceManager = (_game.Services.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager); - if (deviceManager != null) - { - if (deviceManager.PreferredBackBufferWidth > deviceManager.PreferredBackBufferHeight) - return (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); - else - return (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); - } - - return (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); - } - - return _supportedOrientations; - } - /// /// Updates the screen orientation. Filters out requests for unsupported orientations. /// - internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphicsChanges) + internal void SetOrientation(DisplayOrientation newOrientation, DisplayOrientation supported, bool applyGraphicsChanges) { - DisplayOrientation supported = GetEffectiveSupportedOrientations(); - // If the new orientation is not supported, force a supported orientation - if ((supported & newOrientation) == 0) + if ((newOrientation & supported) == 0) { if ((supported & DisplayOrientation.LandscapeLeft) != 0) newOrientation = DisplayOrientation.LandscapeLeft; @@ -306,7 +285,7 @@ internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphi newOrientation = DisplayOrientation.PortraitDown; } - if ((supported & newOrientation) != 0) + if ((newOrientation & supported) != 0) { DisplayOrientation oldOrientation = CurrentOrientation; _currentOrientation = newOrientation; @@ -316,7 +295,7 @@ internal void SetOrientation(DisplayOrientation newOrientation, bool applyGraphi TouchPanel.DisplayOrientation = newOrientation; - if (applyGraphicsChanges && (oldOrientation != newOrientation)) + if ((newOrientation != oldOrientation) && applyGraphicsChanges) { GraphicsDeviceManager gdm = ((IPlatformGame)_game).GetStrategy().GraphicsDeviceManager; if (gdm != null) @@ -424,6 +403,7 @@ public void Dispose() _activity.WindowFocused += Activity_WindowFocused; _activity.WindowUnfocused += Activity_WindowUnfocused; + _activity.WindowOrientationChanged += Activity_WindowOrientationChanged; _activity = null; } diff --git a/Platforms/Game/.CardboardLegacy/ConcreteGraphicsDeviceManager.cs b/Platforms/Game/.CardboardLegacy/ConcreteGraphicsDeviceManager.cs index e379d2652f7..8f9f0b56527 100644 --- a/Platforms/Game/.CardboardLegacy/ConcreteGraphicsDeviceManager.cs +++ b/Platforms/Game/.CardboardLegacy/ConcreteGraphicsDeviceManager.cs @@ -66,9 +66,6 @@ public override DisplayOrientation SupportedOrientations set { base.SupportedOrientations = value; - - if (base.Game.Window != null) - ((AndroidGameWindow)base.Game.Window)._supportedOrientations = base.SupportedOrientations; } } @@ -111,7 +108,15 @@ public override void CreateDevice() // ApplyChanges { // Trigger a change in orientation in case the supported orientations have changed - androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, false); + DisplayOrientation supported = this.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, supported, false); base.GraphicsDevice.PresentationParameters.DisplayOrientation = base.Game.Window.CurrentOrientation; @@ -136,7 +141,15 @@ public override void CreateDevice() Android.App.Activity activity = AndroidGameWindow.Activity; DisplayOrientation currentOrientation = AndroidCompatibility.Current.GetAbsoluteOrientation(activity); - androidGameWindow.SetOrientation(currentOrientation, false); + DisplayOrientation supported2 = this.SupportedOrientations; + if (supported2 == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported2 = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported2 = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(currentOrientation, supported2, false); // TODO: check if the PreferredBackBufferWidth/Hight is supported and throw an error similar to fullscreen Windows Desktop. base.GraphicsDevice.PresentationParameters.BackBufferWidth = surfaceView.Width; @@ -165,7 +178,15 @@ public override void ApplyChanges() View surfaceView = androidGameWindow.GameView; // Trigger a change in orientation in case the supported orientations have changed - androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, false); + DisplayOrientation supported = this.SupportedOrientations; + if (supported == DisplayOrientation.Default) + { + if (this.PreferredBackBufferWidth <= this.PreferredBackBufferHeight) + supported = (DisplayOrientation.Portrait | DisplayOrientation.PortraitDown); + else + supported = (DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight); + } + androidGameWindow.SetOrientation(base.Game.Window.CurrentOrientation, supported, false); base.GraphicsDevice.PresentationParameters.DisplayOrientation = base.Game.Window.CurrentOrientation; diff --git a/Platforms/Kni.Platform.Android.GL.Xamarin.csproj b/Platforms/Kni.Platform.Android.GL.Xamarin.csproj index dc5910a584b..c255bb98259 100644 --- a/Platforms/Kni.Platform.Android.GL.Xamarin.csproj +++ b/Platforms/Kni.Platform.Android.GL.Xamarin.csproj @@ -197,6 +197,7 @@ + diff --git a/Platforms/Kni.Platform.Android.GL.csproj b/Platforms/Kni.Platform.Android.GL.csproj index 5246f664ce9..3857ad3348c 100644 --- a/Platforms/Kni.Platform.Android.GL.csproj +++ b/Platforms/Kni.Platform.Android.GL.csproj @@ -118,6 +118,7 @@ + diff --git a/Platforms/Kni.Platform.Cardboard.GL.csproj b/Platforms/Kni.Platform.Cardboard.GL.csproj index 327dd58f056..4a9da31b463 100644 --- a/Platforms/Kni.Platform.Cardboard.GL.csproj +++ b/Platforms/Kni.Platform.Cardboard.GL.csproj @@ -162,6 +162,7 @@ + diff --git a/Platforms/Kni.Platform.Oculus.GL.csproj b/Platforms/Kni.Platform.Oculus.GL.csproj index 57e01b93158..6740f1ebeec 100644 --- a/Platforms/Kni.Platform.Oculus.GL.csproj +++ b/Platforms/Kni.Platform.Oculus.GL.csproj @@ -118,6 +118,7 @@ +