Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix obsolete code in Android safe area handling by using AndroidX #6175

Merged
merged 5 commits into from Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions osu.Framework.Android.props
Expand Up @@ -7,5 +7,6 @@
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<NoWarn>$(NoWarn);XA4218</NoWarn>
</PropertyGroup>
</Project>
13 changes: 13 additions & 0 deletions osu.Framework.Android/AndroidExtensions.cs
@@ -0,0 +1,13 @@
// 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.Graphics;
using osu.Framework.Graphics.Primitives;

namespace osu.Framework.Android
{
public static class AndroidExtensions
{
public static RectangleI ToRectangleI(this Rect rect) => new RectangleI(rect.Left, rect.Top, rect.Width(), rect.Height());
}
}
88 changes: 31 additions & 57 deletions osu.Framework.Android/AndroidGameView.cs
Expand Up @@ -4,19 +4,18 @@
using System;
using System.Runtime.Versioning;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Views.InputMethods;
using AndroidX.Core.View;
using AndroidX.Window.Layout;
using osu.Framework.Android.Input;
using osu.Framework.Logging;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Platform;
using osuTK.Graphics;

Expand Down Expand Up @@ -188,25 +187,18 @@ protected override void OnLoad(EventArgs e)
RenderGame();
}

public override WindowInsets? OnApplyWindowInsets(WindowInsets? insets)
{
updateSafeArea(insets);
return base.OnApplyWindowInsets(insets);
}

[STAThread]
public void RenderGame()
{
// request focus so that joystick input can immediately work.
RequestFocus();

LayoutChange += (_, _) => updateSafeArea();

Activity.IsActive.BindValueChanged(active =>
{
// When rotating device 180 degrees in the background,
// LayoutChange doesn't trigger after returning to game.
// So update safe area once active again.
if (active.NewValue)
updateSafeArea();
Comment on lines -199 to -205
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this scenario on two of my devices, and there are no issues when using OnApplyWindowInsets().

});

updateSafeArea();

Host = new AndroidGameHost(this);
Host.ExceptionThrown += handleException;
Host.Run(game);
Expand All @@ -226,72 +218,54 @@ private bool handleException(Exception ex)
/// <summary>
/// Updates the <see cref="IWindow.SafeAreaPadding"/>, taking into account screen insets that may be obstructing this <see cref="AndroidGameView"/>.
/// </summary>
private void updateSafeArea()
private void updateSafeArea(WindowInsets? windowInsets)
{
// compute the usable screen area.

var screenSize = new Point();
#pragma warning disable 618 // GetRealSize is deprecated
Display.AsNonNull().GetRealSize(screenSize);
#pragma warning restore 618
var screenArea = new RectangleI(0, 0, screenSize.X, screenSize.Y);
var usableScreenArea = screenArea;
var metrics = WindowMetricsCalculator.Companion.OrCreate.ComputeCurrentWindowMetrics(Activity);
var windowArea = metrics.Bounds.ToRectangleI();
var usableWindowArea = windowArea;

if (OperatingSystem.IsAndroidVersionAtLeast(28))
{
var cutout = RootWindowInsets?.DisplayCutout;
var cutout = windowInsets?.DisplayCutout;

if (cutout != null)
usableScreenArea = usableScreenArea.Shrink(cutout.SafeInsetLeft, cutout.SafeInsetRight, cutout.SafeInsetTop, cutout.SafeInsetBottom);
usableWindowArea = usableWindowArea.Shrink(cutout.SafeInsetLeft, cutout.SafeInsetRight, cutout.SafeInsetTop, cutout.SafeInsetBottom);
}

if (OperatingSystem.IsAndroidVersionAtLeast(31) && RootWindowInsets != null)
if (OperatingSystem.IsAndroidVersionAtLeast(31) && windowInsets != null)
{
var topLeftCorner = RootWindowInsets.GetRoundedCorner((int)RoundedCornerPosition.TopLeft);
var topRightCorner = RootWindowInsets.GetRoundedCorner((int)RoundedCornerPosition.TopRight);
var bottomLeftCorner = RootWindowInsets.GetRoundedCorner((int)RoundedCornerPosition.BottomLeft);
var bottomRightCorner = RootWindowInsets.GetRoundedCorner((int)RoundedCornerPosition.BottomRight);
var topLeftCorner = windowInsets.GetRoundedCorner((int)RoundedCornerPosition.TopLeft);
var topRightCorner = windowInsets.GetRoundedCorner((int)RoundedCornerPosition.TopRight);
var bottomLeftCorner = windowInsets.GetRoundedCorner((int)RoundedCornerPosition.BottomLeft);
var bottomRightCorner = windowInsets.GetRoundedCorner((int)RoundedCornerPosition.BottomRight);

int cornerInsetLeft = Math.Max(topLeftCorner?.Radius ?? 0, bottomLeftCorner?.Radius ?? 0);
int cornerInsetRight = Math.Max(topRightCorner?.Radius ?? 0, bottomRightCorner?.Radius ?? 0);
int cornerInsetTop = Math.Max(topLeftCorner?.Radius ?? 0, topRightCorner?.Radius ?? 0);
int cornerInsetBottom = Math.Max(bottomLeftCorner?.Radius ?? 0, bottomRightCorner?.Radius ?? 0);

var radiusInsetArea = screenArea.Width >= screenArea.Height
? screenArea.Shrink(cornerInsetLeft, cornerInsetRight, 0, 0)
: screenArea.Shrink(0, 0, cornerInsetTop, cornerInsetBottom);
var radiusInsetArea = windowArea.Width >= windowArea.Height
? windowArea.Shrink(cornerInsetLeft, cornerInsetRight, 0, 0)
: windowArea.Shrink(0, 0, cornerInsetTop, cornerInsetBottom);

usableScreenArea = usableScreenArea.Intersect(radiusInsetArea);
usableWindowArea = usableWindowArea.Intersect(radiusInsetArea);
}

if (OperatingSystem.IsAndroidVersionAtLeast(24) && Activity.IsInMultiWindowMode)
if (OperatingSystem.IsAndroidVersionAtLeast(24) && Activity.IsInMultiWindowMode && windowInsets != null)
{
// if we are in multi-window mode, the status bar is always visible (even if we request to hide it) and could be obstructing our view.
// if multi-window mode is not active, we can assume the status bar is hidden so we shouldn't consider it for safe area calculations.

// `SystemWindowInsetTop` should be the correct inset here, but it doesn't correctly work (gives `0` even if the view is obstructed).
#pragma warning disable 618 // StableInsetTop is deprecated
int statusBarHeight = RootWindowInsets?.StableInsetTop ?? 0;
#pragma warning restore 618 //
usableScreenArea = usableScreenArea.Intersect(screenArea.Shrink(0, 0, statusBarHeight, 0));
var insetsCompat = WindowInsetsCompat.ToWindowInsetsCompat(windowInsets, this);
int statusBarHeight = insetsCompat.GetInsets(WindowInsetsCompat.Type.StatusBars()).Top;
usableWindowArea = usableWindowArea.Intersect(windowArea.Shrink(0, 0, statusBarHeight, 0));
}

// compute the location/area of this view on the screen.

int[] location = new int[2];
GetLocationOnScreen(location);
var viewArea = new RectangleI(location[0], location[1], ((View)this).Width, ((View)this).Height);

// intersect with the usable area and treat the the difference as unsafe.

var usableViewArea = viewArea.Intersect(usableScreenArea);

SafeAreaPadding.Value = new MarginPadding
{
Left = usableViewArea.Left - viewArea.Left,
Top = usableViewArea.Top - viewArea.Top,
Right = viewArea.Right - usableViewArea.Right,
Bottom = viewArea.Bottom - usableViewArea.Bottom,
Left = usableWindowArea.Left - windowArea.Left,
Top = usableWindowArea.Top - windowArea.Top,
Right = windowArea.Right - usableWindowArea.Right,
Bottom = windowArea.Bottom - usableWindowArea.Bottom,
};
}

Expand Down
1 change: 1 addition & 0 deletions osu.Framework.Android/osu.Framework.Android.csproj
Expand Up @@ -19,5 +19,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osuTK.Android" Version="1.0.211" />
<PackageReference Include="Xamarin.AndroidX.Window" Version="1.2.0.1" />
</ItemGroup>
</Project>