diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs index 4ee900175195..7380e06458fa 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs @@ -130,42 +130,61 @@ public override WindowInsetsCompat OnApplyWindowInsets(AView v, WindowInsetsComp if (insets == null || v == null) return insets; - if (v is CoordinatorLayout) + if (FlyoutView is IHandleWindowInsets handleWindowInsets) + { + return base.OnApplyWindowInsets(v, insets); + } + + if (v is CoordinatorLayout && FlyoutView is not null) { // The flyout overlaps the status bar so we don't really care about insetting it var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); var displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); var topInset = Math.Max(systemBars?.Top ?? 0, displayCutout?.Top ?? 0); var bottomInset = Math.Max(systemBars?.Bottom ?? 0, displayCutout?.Bottom ?? 0); + var leftInset = Math.Max(systemBars?.Left ?? 0, displayCutout?.Left ?? 0); var appbarLayout = v.FindDescendantView((v) => true); int flyoutViewBottomInset = 0; if (FooterView is not null) { - v.SetPadding(0, 0, 0, bottomInset); flyoutViewBottomInset = 0; + if (appbarLayout.MeasuredHeight > 0) + { + // When AppBarLayout exists, let it handle the top inset + v.SetPadding(leftInset, 0, 0, bottomInset); + appbarLayout?.SetPadding(0, topInset, 0, 0); + } + else + { + // No AppBarLayout, root view handles top inset + v.SetPadding(leftInset, topInset, 0, bottomInset); + appbarLayout?.SetPadding(0, 0, 0, 0); + } } else { flyoutViewBottomInset = bottomInset; - v.SetPadding(0, 0, 0, 0); + if (appbarLayout.MeasuredHeight > 0) + { + // When AppBarLayout exists, let it handle the top inset + v.SetPadding(leftInset, 0, 0, 0); + appbarLayout?.SetPadding(0, topInset, 0, 0); + } + else + { + // No AppBarLayout, root view handles top inset + v.SetPadding(leftInset, topInset, 0, 0); + appbarLayout?.SetPadding(0, 0, 0, 0); + } } - if (appbarLayout.MeasuredHeight > 0) - { - FlyoutView?.SetPadding(0, 0, 0, flyoutViewBottomInset); - appbarLayout?.SetPadding(0, topInset, 0, 0); - } - else - { - FlyoutView?.SetPadding(0, topInset, 0, flyoutViewBottomInset); - appbarLayout?.SetPadding(0, 0, 0, 0); - } + FlyoutView.SetPadding(0, 0, 0, flyoutViewBottomInset); if (_bgImageRef != null && _bgImageRef.TryGetTarget(out var bgImage) && bgImage != null) { - bgImage.SetPadding(0, topInset, 0, bottomInset); + bgImage.SetPadding(leftInset, topInset, 0, bottomInset); } return WindowInsetsCompat.Consumed; diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/ShellFlyoutShouldRespectSafeArea.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/ShellFlyoutShouldRespectSafeArea.png new file mode 100644 index 000000000000..47ea49aac19d Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/ShellFlyoutShouldRespectSafeArea.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellFlyoutShouldRespectSafeArea.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellFlyoutShouldRespectSafeArea.png new file mode 100644 index 000000000000..70079eff088a Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellFlyoutShouldRespectSafeArea.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldFlyoutTextWrapsInLandscape.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldFlyoutTextWrapsInLandscape.png index 2b4abe0ef731..403a2bf575bf 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldFlyoutTextWrapsInLandscape.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldFlyoutTextWrapsInLandscape.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyShellFlyoutBackgroundImage.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyShellFlyoutBackgroundImage.png index e68af44e1fe9..b1bd9541263a 100644 Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyShellFlyoutBackgroundImage.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyShellFlyoutBackgroundImage.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs new file mode 100644 index 000000000000..0cc97c7c2f42 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs @@ -0,0 +1,42 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 32275, "[NET10] SafeAreaEdges cannot be set for Shell and the flyout menu collides with display notch and status bar in landscape mode", PlatformAffected.Android)] +public class Issue32275 : TestShell +{ + protected override void Init() + { + FlyoutBehavior = FlyoutBehavior.Flyout; + + for (int i = 0; i < 10; i++) + { + Items.Add(new FlyoutItem + { + Title = "Issue 32275", + Items = + { + new ShellContent + { + Content = new Issue32275ContentPage(), + } + } + }); + } + } + + public class Issue32275ContentPage : TestContentPage + { + protected override void Init() + { + Title = "Issue 32275"; + Content = new StackLayout + { + Children = + { + new Label { AutomationId = "Issue32275Label", Text = "Open the flyout menu in landscape mode on a device with a display notch or status bar. The flyout menu should not collide with the notch or status bar." } + } + }; + } + } +} + + diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32275_Template.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275_Template.cs new file mode 100644 index 000000000000..2fb5d5590266 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275_Template.cs @@ -0,0 +1,49 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.None, 32275, "SafeAreaEdges should be handled by the FlyoutContentTemplate", PlatformAffected.Android)] +public class Issue32275_Template : TestShell +{ + protected override void Init() + { + FlyoutBehavior = FlyoutBehavior.Flyout; + + var page = new Issue32275_Template_ContentPage(); + + AddFlyoutItem(page, "Flyout Item"); + + + FlyoutContentTemplate = new DataTemplate(() => + { + var stackLayout = new StackLayout() { BackgroundColor = Colors.Gray, SafeAreaEdges = SafeAreaEdges.All }; + + for (int i = 0; i < 50; i++) + { + var label = new Label + { + Text = $"Flyout Item {i + 1}", + FontSize = 24, + }; + + stackLayout.Children.Add(label); + } + return stackLayout; + }); + } + + public class Issue32275_Template_ContentPage : TestContentPage + { + protected override void Init() + { + Title = "Issue 32275"; + Content = new StackLayout + { + Children = + { + new Label { AutomationId = "Issue32275_Template_Label", Text = "Open the flyout menu in landscape mode on a device with a display notch or status bar. The flyout menu should not collide with the notch or status bar. The default SafeAreaPadding should not be applied. It should be handled by the template itself." } + } + }; + } + } +} + + diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs new file mode 100644 index 000000000000..c33b58c3de8c --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs @@ -0,0 +1,30 @@ +#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // Ignore file for Catalyst and Windows platforms. This test is applicable for mobile platforms only. +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue32275 : _IssuesUITest +{ + public Issue32275(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "[NET10] SafeAreaEdges cannot be set for Shell and the flyout menu collides with display notch and status bar in landscape mode"; + + [Test] + [Category(UITestCategories.SafeAreaEdges)] + public void ShellFlyoutShouldRespectSafeArea() + { + App.WaitForElement("Issue32275Label"); + App.SetOrientationLandscape(); + App.TapShellFlyoutIcon(); +#if ANDROID + VerifyScreenshot(cropLeft: 125); +#else + VerifyScreenshot(); +#endif + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275_Template.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275_Template.cs new file mode 100644 index 000000000000..05d9a5431ded --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275_Template.cs @@ -0,0 +1,31 @@ +#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // Ignore file for Catalyst and Windows platforms. This test is applicable for mobile platforms only. +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue32275_Template : _IssuesUITest +{ + public Issue32275_Template(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "SafeAreaEdges should be handled by the FlyoutContentTemplate"; + + [Test] + [Category(UITestCategories.SafeAreaEdges)] + public void ShellFlyoutContentTemplateShouldRespectSafeArea() + { + App.WaitForElement("Issue32275_Template_Label"); + App.TapShellFlyoutIcon(); + App.SetOrientationLandscape(); + Thread.Sleep(1000); // Allow time for orientation change to take effect and UI to stabilize. +#if ANDROID + VerifyScreenshot(cropLeft: 125); +#else + VerifyScreenshot(); +#endif + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellFlyoutShouldRespectSafeArea.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellFlyoutShouldRespectSafeArea.png new file mode 100644 index 000000000000..5172df6d5e65 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellFlyoutShouldRespectSafeArea.png differ