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: Android 15 window insets calculation #19474

Merged
merged 8 commits into from
Feb 11, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="uno.platform.unosampleapp" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="35" />
<!-- NOTE: Add permissions in AssemblyInfo.cs -->
<application android:label="SamplesApp" android:usesCleartextTraffic="true">
<!-- Set an API key locally to test the MapControl - https://developers.google.com/maps/documentation/android-sdk/get-api-key -->
Expand Down
13 changes: 10 additions & 3 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml\WindowTests\Window_Metrics.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml\XamlRoot\XamlRoot_Properties.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -2389,7 +2393,7 @@
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\PersonPictureTests\PersonPictureLateBindingPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\Pivot\Pivot_CustomContent_Automated.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -6556,6 +6560,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml\WindowTests\Window_SetBackground.xaml.cs">
<DependentUpon>Window_SetBackground.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml\WindowTests\Window_Metrics.xaml.cs">
<DependentUpon>Window_Metrics.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml\XamlRoot\XamlRoot_Properties.xaml.cs">
<DependentUpon>XamlRoot_Properties.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -7214,7 +7221,7 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\PersonPictureTests\PersonPictureLateBindingPage.xaml.cs">
<DependentUpon>PersonPictureLateBindingPage.xaml</DependentUpon>
</Compile>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\Pivot\Pivot_CustomContent_Automated.xaml.cs">
<DependentUpon>Pivot_CustomContent_Automated.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -9926,4 +9933,4 @@
</Compile>
</ItemGroup>
<Import Project="ItemExclusions.props" />
</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<UserControl
x:Class="UITests.Windows_UI_Xaml.WindowTests.Window_Metrics"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:UITests.Windows_UI_Xaml.WindowTests"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:Uno.UI.Toolkit"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">

<StackPanel>
<CheckBox x:Name="ExtendsIntoTitleBarCheckBox" IsChecked="False" />
<Button Click="{x:Bind GetMetricsClick}">Get metrics</Button>
<TextBlock TextWrapping="Wrap">
<Run Text="AppWindow.Size:" />
<Run x:Name="AppWindowSize" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="AppWindow.Position:" />
<Run x:Name="AppWindowPosition" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="AppWindow.ClientSize:" />
<Run x:Name="AppWindowClientSize" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="Window.Bounds:" />
<Run x:Name="WindowBounds" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="XamlRoot.Size:" />
<Run x:Name="XamlRootSize" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="TitleBar.Height:" />
<Run x:Name="TitleBarHeight" />
</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run Text="VisibleBoundsPadding:" />
<Run x:Name="VisibleBoundsPaddingValue" />
</TextBlock>

<Grid x:Name="PaddedGrid" ui:VisibleBoundsPadding.PaddingMask="All"></Grid>
</StackPanel>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Uno.UI.Samples.Controls;
using Uno.UI.Toolkit;

namespace UITests.Windows_UI_Xaml.WindowTests
{
[Sample("Windowing")]
public sealed partial class Window_Metrics : UserControl
{
private Window _window;

public Window_Metrics()
{
this.InitializeComponent();

_window = SamplesApp.App.MainWindow;
ExtendsIntoTitleBarCheckBox.IsChecked = _window.ExtendsContentIntoTitleBar;
ExtendsIntoTitleBarCheckBox.Checked += (s, e) => _window.ExtendsContentIntoTitleBar = true;
ExtendsIntoTitleBarCheckBox.Unchecked += (s, e) => _window.ExtendsContentIntoTitleBar = false;

this.Loaded += Window_Metrics_Loaded;
}

private void Window_Metrics_Loaded(object sender, RoutedEventArgs e) => GetMetricsClick();

private void GetMetricsClick()
{
#if HAS_UNO_WINUI // AppWindow APIs are only available in WinUI flavor of Uno Platform
AppWindowSize.Text = GetSafe(() => $"{_window.AppWindow.Size.Width:0.##} x {_window.AppWindow.Size.Height:0.##}");
AppWindowPosition.Text = GetSafe(() => $"{_window.AppWindow.Position.X:0.##}, {_window.AppWindow.Position.Y:0.##}");
AppWindowClientSize.Text = GetSafe(() => $"{_window.AppWindow.ClientSize.Width:0.##} x {_window.AppWindow.ClientSize.Height:0.##}");
TitleBarHeight.Text = GetSafe(() => $"{_window.AppWindow.TitleBar.Height:0.##}");
#endif
WindowBounds.Text = GetSafe(() => $"{_window.Bounds.X:0.##}, {_window.Bounds.Y:0.##}, {_window.Bounds.Width:0.##}, {_window.Bounds.Height:0.##}");
XamlRootSize.Text = GetSafe(() => $"{XamlRoot.Size.Width:0.##} x {XamlRoot.Size.Height:0.##}");
var padding = VisibleBoundsPadding.WindowPadding;
VisibleBoundsPaddingValue.Text = GetSafe(() => $"{padding.Left:0.##}, {padding.Top:0.##}, {padding.Right:0.##}, {padding.Bottom:0.##}");
}

private string GetSafe(Func<string> getter)
{
try
{
return getter();
}
catch (Exception)
{
return "N/A";
}
}
}
}
11 changes: 11 additions & 0 deletions src/Uno.UI/UI/Xaml/Application.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Windows.Foundation.Metadata;
using Windows.Globalization;
using Microsoft.UI.Xaml.Controls.Primitives;
using Windows.UI.ViewManagement;
using Colors = Microsoft.UI.Colors;

namespace Microsoft.UI.Xaml;

Expand All @@ -28,4 +30,13 @@ static partial void StartPartial(ApplicationInitializationCallback callback)
/// See - https://stackoverflow.com/a/3987733/732221
/// </remarks>
private DateTimeOffset GetSuspendingOffset() => DateTimeOffset.Now.AddSeconds(5);

partial void ApplySystemOverlaysTheming()
{
var requestedTheme = InternalRequestedTheme;

StatusBar.GetForCurrentView().ForegroundColor = requestedTheme == ApplicationTheme.Dark
? Colors.White
: Colors.Black;
}
}
8 changes: 7 additions & 1 deletion src/Uno.UI/UI/Xaml/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,13 @@ private void SetRequestedTheme(ApplicationTheme requestedTheme)

internal void UpdateResourceBindingsForHotReload() => OnResourcesChanged(ResourceUpdateReason.HotReload);

internal void OnRequestedThemeChanged() => OnResourcesChanged(ResourceUpdateReason.ThemeResource);
internal void OnRequestedThemeChanged()
{
ApplySystemOverlaysTheming();
OnResourcesChanged(ResourceUpdateReason.ThemeResource);
}

partial void ApplySystemOverlaysTheming();

private void UpdateRootElementBackground()
{
Expand Down
2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/ApplicationActivity.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public override void OnAttachedToWindow()
{
base.OnAttachedToWindow();

StatusBar.GetForCurrentView().UpdateSystemUiVisibility();

// Cannot call this in ctor: see
// https://stackoverflow.com/questions/10593022/monodroid-error-when-calling-constructor-of-custom-view-twodscrollview#10603714
RaiseConfigurationChanges();
Expand Down
76 changes: 49 additions & 27 deletions src/Uno.UI/UI/Xaml/Window/Native/NativeWindowWrapper.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using AndroidX.AppCompat.App;
using AndroidX.Core.View;
using Uno.Disposables;
using Uno.Foundation.Logging;
using Uno.UI.Extensions;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
Expand Down Expand Up @@ -72,24 +73,24 @@ internal bool IsStatusBarTranslucent()

internal void RaiseNativeSizeChanged()
{
var (windowSize, visibleBounds, trueVisibleBounds) = GetVisualBounds();
var (windowSize, visibleBounds) = GetVisualBounds();

Bounds = new Rect(default, windowSize);
VisibleBounds = visibleBounds;
Size = new((int)(windowSize.Width * RasterizationScale), (int)(windowSize.Height * RasterizationScale));

if (_previousTrueVisibleBounds != trueVisibleBounds)
if (_previousTrueVisibleBounds != visibleBounds)
{
_previousTrueVisibleBounds = trueVisibleBounds;
_previousTrueVisibleBounds = visibleBounds;

// TODO: Adjust when multiple windows are supported on Android #13827
ApplicationView.GetForCurrentView()?.SetTrueVisibleBounds(trueVisibleBounds);
ApplicationView.GetForCurrentView()?.SetTrueVisibleBounds(visibleBounds);
}
}

protected override void ShowCore() => RemovePreDrawListener();

private (Size windowSize, Rect visibleBounds, Rect trueVisibleBounds) GetVisualBounds()
private (Size windowSize, Rect visibleBounds) GetVisualBounds()
{
if (ContextHelper.Current is not Activity activity)
{
Expand All @@ -99,28 +100,54 @@ internal void RaiseNativeSizeChanged()
var windowInsets = GetWindowInsets(activity);

var insetsTypes = WindowInsetsCompat.Type.SystemBars() | WindowInsetsCompat.Type.DisplayCutout(); // == WindowInsets.Type.StatusBars() | WindowInsets.Type.NavigationBars() | WindowInsets.Type.CaptionBar();
Rect windowBounds;
Rect visibleBounds;

var opaqueInsetsTypes = insetsTypes;
if (IsStatusBarTranslucent())
if ((int)Android.OS.Build.VERSION.SdkInt < 35)
{
opaqueInsetsTypes &= ~WindowInsetsCompat.Type.StatusBars();
var opaqueInsetsTypes = insetsTypes;
if (IsStatusBarTranslucent())
{
opaqueInsetsTypes &= ~WindowInsetsCompat.Type.StatusBars();
}
if (IsNavigationBarTranslucent())
{
opaqueInsetsTypes &= ~WindowInsetsCompat.Type.NavigationBars();
}

var insets = windowInsets?.GetInsets(insetsTypes).ToThickness() ?? default;
var opaqueInsets = windowInsets?.GetInsets(opaqueInsetsTypes).ToThickness() ?? default;
var translucentInsets = insets.Minus(opaqueInsets);

// The native display size does not include any insets, so we remove the "opaque" insets under which we cannot draw anything
windowBounds = new Rect(default, GetWindowSize().Subtract(opaqueInsets));

// The visible bounds is the windows bounds on which we remove also translucentInsets
visibleBounds = windowBounds.DeflateBy(translucentInsets);
}
if (IsNavigationBarTranslucent())
else
{
opaqueInsetsTypes &= ~WindowInsetsCompat.Type.NavigationBars();
}
var insets = windowInsets?.GetInsets(insetsTypes).ToThickness() ?? default;

var insets = windowInsets?.GetInsets(insetsTypes).ToThickness() ?? default;
var opaqueInsets = windowInsets?.GetInsets(opaqueInsetsTypes).ToThickness() ?? default;
var translucentInsets = insets.Minus(opaqueInsets);
if (this.Log().IsEnabled(LogLevel.Debug))
{
this.Log().LogDebug($"Insets: {insets}");
}

// The native display size does not include any insets, so we remove the "opaque" insets under which we cannot draw anything
var windowBounds = new Rect(default, GetDisplaySize().Subtract(opaqueInsets));
// Edge-to-edge is default on Android 15 and above
windowBounds = new Rect(default, GetWindowSize());
visibleBounds = windowBounds.DeflateBy(insets);
}

// The visible bounds is the windows bounds on which we remove also translucentInsets
var visibleBounds = windowBounds.DeflateBy(translucentInsets);
if (this.Log().IsEnabled(LogLevel.Debug))
{
this.Log().LogDebug($"WindowBounds: {windowBounds}, VisibleBounds {visibleBounds}");
}

var windowBoundsLogical = windowBounds.PhysicalToLogicalPixels();
var visibleBoundsLogical = visibleBounds.PhysicalToLogicalPixels();

return (windowBounds.PhysicalToLogicalPixels().Size, visibleBounds.PhysicalToLogicalPixels(), visibleBounds.PhysicalToLogicalPixels());
return (windowBoundsLogical.Size, visibleBoundsLogical);
}

private bool IsNavigationBarTranslucent()
Expand Down Expand Up @@ -151,7 +178,7 @@ private WindowInsetsCompat GetWindowInsets(Activity activity)
return null;
}

private Size GetDisplaySize()
private Size GetWindowSize()
{
if (ContextHelper.Current is not Activity activity)
{
Expand All @@ -166,13 +193,6 @@ private Size GetDisplaySize()
displaySize = new Size(windowMetrics.Bounds.Width(), windowMetrics.Bounds.Height());
}
else
{
SetDisplaySizeLegacy();
}

return displaySize;

void SetDisplaySizeLegacy()
{
using var realMetrics = new DisplayMetrics();

Expand All @@ -184,6 +204,8 @@ void SetDisplaySizeLegacy()

displaySize = new Size(realMetrics.WidthPixels, realMetrics.HeightPixels);
}

return displaySize;
}

protected override IDisposable ApplyFullScreenPresenter()
Expand Down
Loading
Loading