Browse Source

Fix insets on android 15 (#18844)

* fix insets on android 15

* add api diff

* fix nit
release/11.3.1
Emmanuel Hansen 8 months ago
committed by Julien Lebosquain
parent
commit
c02b2b8a20
  1. 12
      api/Avalonia.nupkg.xml
  2. 6
      samples/ControlCatalog/MainView.xaml.cs
  3. 4
      samples/SafeAreaDemo/ViewModels/MainViewModel.cs
  4. 68
      src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
  5. 17
      src/Avalonia.Controls/Platform/IInsetsManager.cs
  6. 2
      src/Browser/Avalonia.Browser/BrowserInsetsManager.cs
  7. 2
      src/iOS/Avalonia.iOS/InsetsManager.cs

12
api/Avalonia.nupkg.xml

@ -103,6 +103,18 @@
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left> <Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right> <Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Controls.Platform.IInsetsManager.DisplayEdgeToEdgePreference</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Controls.Platform.IInsetsManager.DisplaysEdgeToEdge</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0009</DiagnosticId> <DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Diagnostics.StyleDiagnostics</Target> <Target>T:Avalonia.Diagnostics.StyleDiagnostics</Target>

6
samples/ControlCatalog/MainView.xaml.cs

@ -108,14 +108,14 @@ namespace ControlCatalog
ViewModel.SafeAreaPadding = insets.SafeAreaPadding; ViewModel.SafeAreaPadding = insets.SafeAreaPadding;
}; };
ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdge; ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdgePreference;
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
ViewModel.PropertyChanged += async (sender, args) => ViewModel.PropertyChanged += async (sender, args) =>
{ {
if (args.PropertyName == nameof(ViewModel.DisplayEdgeToEdge)) if (args.PropertyName == nameof(ViewModel.DisplayEdgeToEdge))
{ {
insets.DisplayEdgeToEdge = ViewModel.DisplayEdgeToEdge; insets.DisplayEdgeToEdgePreference = ViewModel.DisplayEdgeToEdge;
} }
else if (args.PropertyName == nameof(ViewModel.IsSystemBarVisible)) else if (args.PropertyName == nameof(ViewModel.IsSystemBarVisible))
{ {
@ -124,7 +124,7 @@ namespace ControlCatalog
// Give the OS some time to apply new values and refresh the view model. // Give the OS some time to apply new values and refresh the view model.
await Task.Delay(100); await Task.Delay(100);
ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdge; ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdgePreference;
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
}; };
} }

4
samples/SafeAreaDemo/ViewModels/MainViewModel.cs

@ -72,7 +72,7 @@ namespace SafeAreaDemo.ViewModels
if (_insetsManager != null) if (_insetsManager != null)
{ {
_insetsManager.DisplayEdgeToEdge = value; _insetsManager.DisplayEdgeToEdgePreference = value;
} }
this.RaisePropertyChanged(); this.RaisePropertyChanged();
@ -129,7 +129,7 @@ namespace SafeAreaDemo.ViewModels
{ {
_insetsManager.SafeAreaChanged += InsetsManager_SafeAreaChanged; _insetsManager.SafeAreaChanged += InsetsManager_SafeAreaChanged;
_displayEdgeToEdge = _insetsManager.DisplayEdgeToEdge; _displayEdgeToEdge = _insetsManager.DisplayEdgeToEdgePreference;
_hideSystemBars = !(_insetsManager.IsSystemBarVisible ?? false); _hideSystemBars = !(_insetsManager.IsSystemBarVisible ?? false);
} }

68
src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs

@ -17,9 +17,12 @@ namespace Avalonia.Android.Platform
{ {
internal sealed class AndroidInsetsManager : WindowInsetsAnimationCompat.Callback, IInsetsManager, IOnApplyWindowInsetsListener, ViewTreeObserver.IOnGlobalLayoutListener, IInputPane internal sealed class AndroidInsetsManager : WindowInsetsAnimationCompat.Callback, IInsetsManager, IOnApplyWindowInsetsListener, ViewTreeObserver.IOnGlobalLayoutListener, IInputPane
{ {
// For now, we check if running under net 9. TODO: remove runtime check when we target net 10
private static bool IsDisplayEdgeToEdgeForced = System.Environment.Version.Major >=9 && Build.VERSION.SdkInt >= (BuildVersionCodes)35;
private readonly Activity _activity; private readonly Activity _activity;
private readonly TopLevelImpl _topLevel; private readonly TopLevelImpl _topLevel;
private bool _displayEdgeToEdge; private bool _displaysEdgeToEdge;
private bool? _systemUiVisibility; private bool? _systemUiVisibility;
private SystemBarTheme? _statusBarTheme; private SystemBarTheme? _statusBarTheme;
private bool? _isDefaultSystemBarLightTheme; private bool? _isDefaultSystemBarLightTheme;
@ -27,6 +30,7 @@ namespace Avalonia.Android.Platform
private InputPaneState _state; private InputPaneState _state;
private Rect _previousRect; private Rect _previousRect;
private Insets? _previousImeInset; private Insets? _previousImeInset;
private bool _displayEdgeToEdgePreference;
private readonly bool _usesLegacyLayouts; private readonly bool _usesLegacyLayouts;
private AndroidWindow Window => _activity.Window ?? throw new InvalidOperationException("Activity.Window must be set."); private AndroidWindow Window => _activity.Window ?? throw new InvalidOperationException("Activity.Window must be set.");
@ -50,29 +54,42 @@ namespace Avalonia.Android.Platform
} }
} }
public bool DisplayEdgeToEdge public bool DisplayEdgeToEdgePreference
{ {
get => _displayEdgeToEdge; get => _displayEdgeToEdgePreference;
set set
{ {
_displayEdgeToEdge = value; _displayEdgeToEdgePreference = value;
if (OperatingSystem.IsAndroidVersionAtLeast(28) && Window.Attributes is { } attributes) UpdateDisplayEdgeToEgdeState();
{ }
attributes.LayoutInDisplayCutoutMode = value ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default; }
}
WindowCompat.SetDecorFitsSystemWindows(Window, !value); private void UpdateDisplayEdgeToEgdeState()
{
if (IsDisplayEdgeToEdgeForced)
{
_displaysEdgeToEdge = true;
return;
}
if (value) _displaysEdgeToEdge = _displayEdgeToEdgePreference;
{
Window.AddFlags(WindowManagerFlags.TranslucentStatus); if (OperatingSystem.IsAndroidVersionAtLeast(28) && Window.Attributes is { } attributes)
Window.AddFlags(WindowManagerFlags.TranslucentNavigation); {
} attributes.LayoutInDisplayCutoutMode = _displayEdgeToEdgePreference ? LayoutInDisplayCutoutMode.ShortEdges : LayoutInDisplayCutoutMode.Default;
else }
{
SystemBarColor = _systemBarColor; WindowCompat.SetDecorFitsSystemWindows(Window, !_displayEdgeToEdgePreference);
}
if (_displayEdgeToEdgePreference)
{
Window.AddFlags(WindowManagerFlags.TranslucentStatus);
Window.AddFlags(WindowManagerFlags.TranslucentNavigation);
}
else
{
SystemBarColor = _systemBarColor;
} }
} }
@ -89,7 +106,7 @@ namespace Avalonia.Android.Platform
_activity.Window?.DecorView.ViewTreeObserver?.AddOnGlobalLayoutListener(this); _activity.Window?.DecorView.ViewTreeObserver?.AddOnGlobalLayoutListener(this);
} }
DisplayEdgeToEdge = false; DisplayEdgeToEdgePreference = false;
ViewCompat.SetWindowInsetsAnimationCallback(Window.DecorView, this); ViewCompat.SetWindowInsetsAnimationCallback(Window.DecorView, this);
} }
@ -105,11 +122,11 @@ namespace Avalonia.Android.Platform
var renderScaling = _topLevel.RenderScaling; var renderScaling = _topLevel.RenderScaling;
var inset = insets.GetInsets( var inset = insets.GetInsets(
_displayEdgeToEdge ? DisplaysEdgeToEdge ?
WindowInsetsCompat.Type.StatusBars() | WindowInsetsCompat.Type.NavigationBars() | WindowInsetsCompat.Type.StatusBars() | WindowInsetsCompat.Type.NavigationBars() |
WindowInsetsCompat.Type.DisplayCutout() : 0); WindowInsetsCompat.Type.DisplayCutout() : 0);
return new Thickness(inset.Left / renderScaling, return new Thickness(inset.Left / renderScaling,
inset.Top / renderScaling, inset.Top / renderScaling,
inset.Right / renderScaling, inset.Right / renderScaling,
inset.Bottom / renderScaling); inset.Bottom / renderScaling);
@ -264,7 +281,10 @@ namespace Avalonia.Android.Platform
{ {
_systemBarColor = value; _systemBarColor = value;
if (_systemBarColor is { } color && !_displayEdgeToEdge && _activity.Window != null) if (IsDisplayEdgeToEdgeForced)
return;
if (_systemBarColor is { } color && !_displaysEdgeToEdge && _activity.Window != null)
{ {
_activity.Window.ClearFlags(WindowManagerFlags.TranslucentStatus); _activity.Window.ClearFlags(WindowManagerFlags.TranslucentStatus);
_activity.Window.ClearFlags(WindowManagerFlags.TranslucentNavigation); _activity.Window.ClearFlags(WindowManagerFlags.TranslucentNavigation);
@ -282,6 +302,10 @@ namespace Avalonia.Android.Platform
} }
} }
public bool DisplayEdgeToEdge { get => DisplaysEdgeToEdge; set => DisplayEdgeToEdgePreference = value; }
public bool DisplaysEdgeToEdge => _displaysEdgeToEdge;
internal void ApplyStatusBarState() internal void ApplyStatusBarState()
{ {
IsSystemBarVisible = _systemUiVisibility; IsSystemBarVisible = _systemUiVisibility;

17
src/Avalonia.Controls/Platform/IInsetsManager.cs

@ -17,8 +17,20 @@ namespace Avalonia.Controls.Platform
/// <summary> /// <summary>
/// Gets or sets whether the window draws edge to edge. behind any visible system bars. /// Gets or sets whether the window draws edge to edge. behind any visible system bars.
/// </summary> /// </summary>
bool DisplayEdgeToEdgePreference { get; set; }
/// <summary>
/// Gets or sets whether the window draws edge to edge. behind any visible system bars.
/// </summary>
[Obsolete("Use DisplayEdgeToEdgePreference")]
bool DisplayEdgeToEdge { get; set; } bool DisplayEdgeToEdge { get; set; }
/// <summary>
/// Gets whether the window is currently displaying edge to edge.
/// </summary>
bool DisplaysEdgeToEdge { get; }
/// <summary> /// <summary>
/// Gets the current safe area padding. /// Gets the current safe area padding.
/// </summary> /// </summary>
@ -39,9 +51,12 @@ namespace Avalonia.Controls.Platform
public abstract class InsetsManagerBase : IInsetsManager public abstract class InsetsManagerBase : IInsetsManager
{ {
public virtual bool? IsSystemBarVisible { get; set; } public virtual bool? IsSystemBarVisible { get; set; }
public virtual bool DisplayEdgeToEdge { get; set; } public virtual bool DisplayEdgeToEdgePreference { get; set; }
public virtual bool DisplayEdgeToEdge { get => DisplaysEdgeToEdge; set => DisplayEdgeToEdgePreference = value; }
public virtual Thickness SafeAreaPadding { get; protected set; } public virtual Thickness SafeAreaPadding { get; protected set; }
public virtual Color? SystemBarColor { get; set; } public virtual Color? SystemBarColor { get; set; }
public virtual bool DisplaysEdgeToEdge => DisplayEdgeToEdgePreference;
public event EventHandler<SafeAreaChangedArgs>? SafeAreaChanged; public event EventHandler<SafeAreaChangedArgs>? SafeAreaChanged;
protected void OnSafeAreaChanged(SafeAreaChangedArgs eventArgs) protected void OnSafeAreaChanged(SafeAreaChangedArgs eventArgs)

2
src/Browser/Avalonia.Browser/BrowserInsetsManager.cs

@ -18,7 +18,7 @@ namespace Avalonia.Browser
} }
} }
public override bool DisplayEdgeToEdge { get; set; } public override bool DisplayEdgeToEdgePreference { get; set; }
public override Thickness SafeAreaPadding public override Thickness SafeAreaPadding
{ {

2
src/iOS/Avalonia.iOS/InsetsManager.cs

@ -35,7 +35,7 @@ internal class InsetsManager : InsetsManagerBase
} }
public event EventHandler<bool>? DisplayEdgeToEdgeChanged; public event EventHandler<bool>? DisplayEdgeToEdgeChanged;
public override bool DisplayEdgeToEdge public override bool DisplayEdgeToEdgePreference
{ {
get => _displayEdgeToEdge; get => _displayEdgeToEdge;
set set

Loading…
Cancel
Save