Skip to content
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 28986, "Test SafeArea Flyout Page for per-edge safe area control", PlatformAffected.Android | PlatformAffected.iOS, issueTestNumber: 8)]
public partial class Issue28986_FlyoutPage : FlyoutPage
{
public Issue28986_FlyoutPage() : base()
{
var page = new Issue28986_ContentPage() { Title = "SafeArea Flyout Test" };
page.ToolbarItems.Add(new ToolbarItem { Text = "Item 1" });
Shell.SetBackgroundColor(page, Colors.Blue);
Detail = new NavigationPage(page);
Flyout = new ContentPage()
{
Title = "Flyout",
Content = new StackLayout
{
Children =
{
new Label { Text = "This is the flyout content" }
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 28986, "Test SafeArea Navigation Page for per-edge safe area control", PlatformAffected.Android | PlatformAffected.iOS, issueTestNumber: 7)]
public partial class Issue28986_NavigationPage : NavigationPage
{
public Issue28986_NavigationPage() : base(new Issue28986_ContentPage())
{
BarBackground = Colors.Blue;
}
}
37 changes: 37 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue28986_Shell.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 28986, "Test SafeArea Shell Page for per-edge safe area control", PlatformAffected.Android | PlatformAffected.iOS, issueTestNumber: 6)]
public partial class Issue28986_Shell : Shell
{
public Issue28986_Shell() : base()
{
var page = new Issue28986_ContentPage();
page.ToolbarItems.Add(new ToolbarItem { Text = "Item 1" });
page.Title = "SafeArea Shell Test";
Shell.SetBackgroundColor(page, Colors.Blue);
Items.Add(new FlyoutItem()
{
Items =
{
new ShellContent()
{
Content = page,
}
}
});

Items.Add(new FlyoutItem()
{
Items =
{
new ShellContent()
{
Content = new ContentPage()
{
Title = "Page 2"
},
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if IOS || ANDROID
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue28986_FlyoutPage : _IssuesUITest
{
public override string Issue => "Test SafeArea Flyout Page for per-edge safe area control";

public Issue28986_FlyoutPage(TestDevice device) : base(device)
{
}

[Test]
[Category(UITestCategories.SafeAreaEdges)]
public void ToolbarExtendsAllTheWayLeftAndRight_FlyoutPage()
{
// 1. Test loads - verify essential elements are present
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
VerifyScreenshot();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if IOS || ANDROID
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue28986_NavigationPage : _IssuesUITest
{
public override string Issue => "Test SafeArea Navigation Page for per-edge safe area control";

public Issue28986_NavigationPage(TestDevice device) : base(device)
{
}

[Test]
[Category(UITestCategories.SafeAreaEdges)]
public void ToolbarExtendsAllTheWayLeftAndRight_NavigationPage()
{
// 1. Test loads - verify essential elements are present
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
VerifyScreenshot();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if IOS || ANDROID
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue28986_Shell : _IssuesUITest
{
public override string Issue => "Test SafeArea Shell Page for per-edge safe area control";

public Issue28986_Shell(TestDevice device) : base(device)
{
}

[Test]
[Category(UITestCategories.SafeAreaEdges)]
public void ToolbarExtendsAllTheWayLeftAndRight_Shell()
{
// 1. Test loads - verify essential elements are present
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
VerifyScreenshot();
}
}
#endif
3 changes: 2 additions & 1 deletion src/Controls/tests/TestCases.Shared.Tests/UITest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ but both can happen.
environmentName = "android-notch-36";
}

if (!((deviceApiLevel == 30 && deviceScreenSize == "1080x1920" && deviceScreenDensity == 420) || (deviceApiLevel == 36 && deviceScreenSize == "1080x2424" && deviceScreenDensity == 420)))
if (!((deviceApiLevel == 30 && (deviceScreenSize.Equals("1080x1920", StringComparison.OrdinalIgnoreCase) || deviceScreenSize.Equals("1920x1080", StringComparison.OrdinalIgnoreCase)) && deviceScreenDensity == 420) ||
(deviceApiLevel == 36 && (deviceScreenSize.Equals("1080x2424", StringComparison.OrdinalIgnoreCase) || deviceScreenSize.Equals("2424x1080", StringComparison.OrdinalIgnoreCase)) && deviceScreenDensity == 420)))
{
Assert.Fail($"Android visual tests should be run on an API30 emulator image with 1080x1920 420dpi screen or API36 emulator image with 1080x2424 420dpi screen, but the current device is API {deviceApiLevel} with a {deviceScreenSize} {deviceScreenDensity}dpi screen. Follow the steps on the MAUI UI testing wiki to launch the Android emulator with the right image.");
}
Expand Down
28 changes: 28 additions & 0 deletions src/Core/src/Handlers/Toolbar/ToolbarHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,39 @@ protected override MaterialToolbar CreatePlatformElement()
return Microsoft.Maui.PlatformInterop.CreateToolbar(context, context.GetActionBarHeight(), -1);
}

protected override void ConnectHandler(MaterialToolbar platformView)
{
base.ConnectHandler(platformView);
platformView.ViewAttachedToWindow += OnViewAttachedToWindow;
platformView.ViewDetachedFromWindow += OnViewDetachedFromWindow;
}


void OnViewDetachedFromWindow(object? sender, View.ViewDetachedFromWindowEventArgs e)
{
if (sender is MaterialToolbar mt && mt.IsAlive() && mt.Context is not null)
{
GlobalWindowInsetListenerExtensions.RemoveGlobalWindowInsetListener(mt, mt.Context);
}
}

void OnViewAttachedToWindow(object? sender, View.ViewAttachedToWindowEventArgs e)
{
var context = MauiContext?.Context ?? throw new InvalidOperationException("Context cannot be null");
GlobalWindowInsetListenerExtensions.TrySetGlobalWindowInsetListener(PlatformView, context);
}

private protected override void OnDisconnectHandler(object platformView)
{
base.OnDisconnectHandler(platformView);

if (platformView is MaterialToolbar mt && mt.IsAlive())
{
mt.RemoveFromParent();
mt.ViewAttachedToWindow -= OnViewAttachedToWindow;
mt.ViewDetachedFromWindow -= OnViewDetachedFromWindow;

};
}

public static void MapTitle(IToolbarHandler arg1, IToolbar arg2)
Expand Down
15 changes: 12 additions & 3 deletions src/Core/src/Platform/Android/GlobalWindowInsetListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public GlobalWindowInsetListener() : base(DispatchModeStop)
var rightInset = Math.Max(systemBars?.Right ?? 0, displayCutout?.Right ?? 0);
var bottomInset = Math.Max(systemBars?.Bottom ?? 0, displayCutout?.Bottom ?? 0);

if (v is MaterialToolbar)
{
v.SetPadding(displayCutout?.Left ?? 0, 0, displayCutout?.Right ?? 0, 0);
return WindowInsetsCompat.Consumed;
}

// Handle special cases
var appBarLayout = v.FindViewById<AppBarLayout>(Resource.Id.navigationlayout_appbar);

Expand Down Expand Up @@ -86,11 +92,14 @@ public GlobalWindowInsetListener() : base(DispatchModeStop)
{
if (appBarLayoutContainsSomething)
{
appBarLayout.SetPadding(leftInset, topInset, rightInset, 0);
// Pad the AppBarLayout to avoid the navigation bar in landscape orientation and system UI in portrait.
// In landscape, the navigation bar is on the left or right edge; in portrait, we account for the status bar and display cutouts.
// Without this padding, the AppBarLayout would extend behind these system UI elements and be partially hidden or non-interactive.
appBarLayout.SetPadding(systemBars?.Left ?? 0, topInset, systemBars?.Right ?? 0, 0);
}
else
{
appBarLayout.SetPadding(leftInset, 0, rightInset, 0);
appBarLayout.SetPadding(0, 0, 0, 0);
}
}

Expand Down Expand Up @@ -302,7 +311,7 @@ internal static class GlobalWindowInsetListenerExtensions
/// <param name="context">The Android context to get the listener from</param>
public static bool TrySetGlobalWindowInsetListener(this View view, Context context)
{
if (view.FindParent(
if (view is not MaterialToolbar && view.FindParent(
(parent) =>
parent is NestedScrollView ||
parent is AppBarLayout ||
Expand Down
1 change: 1 addition & 0 deletions src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Microsoft.Maui.Handlers.OpenWindowRequest.OpenWindowRequest(Microsoft.Maui.Handl
Microsoft.Maui.Hosting.MauiAppBuilder.Environment.get -> Microsoft.Maui.Hosting.MauiHostEnvironment!
Microsoft.Maui.Hosting.MauiAppBuilder.Properties.get -> System.Collections.Generic.IDictionary<object!, object!>!
Microsoft.Maui.PlatformViewGroup
override Microsoft.Maui.Handlers.ToolbarHandler.ConnectHandler(Google.Android.Material.AppBar.MaterialToolbar! platformView) -> void
override Microsoft.Maui.PlatformViewGroup.JniPeerMembers.get -> Java.Interop.JniPeerMembers!
override Microsoft.Maui.PlatformViewGroup.ThresholdClass.get -> nint
override Microsoft.Maui.PlatformViewGroup.ThresholdType.get -> System.Type!
Expand Down