From de12750ff835be87744f56ca9dd6adffe2bc4533 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:46:41 +0000 Subject: [PATCH 01/15] Initial plan From 6607f64fb6640085971a24c1349daf0ae02e631e Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 12 Nov 2025 16:33:25 -0600 Subject: [PATCH 02/15] - Apply fix for emulators with APIs less than 30 --- .../TestCases.HostApp/Issues/Issue32278.xaml | 10 +++ .../Issues/Issue32278.xaml.cs | 84 +++++++++++++++++++ .../Tests/Issues/Issue32278.cs | 48 +++++++++++ .../Android/MauiWindowInsetListener.cs | 45 +++++++++- 4 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml new file mode 100644 index 000000000000..55eaf0af3e81 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs new file mode 100644 index 000000000000..6f9ee20375e5 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs @@ -0,0 +1,84 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 32278, "Shell content page title position incorrect/clipped", PlatformAffected.Android)] +public partial class Issue32278 : Shell +{ + public Issue32278() + { + InitializeComponent(); + Routing.RegisterRoute("NewPage1", typeof(Issue32278NewPage)); + } +} + +public class Issue32278MainPage : ContentPage +{ + public Issue32278MainPage() + { + Title = "MainPage"; + + var button = new Button + { + Text = "Navigate to NewPage1", + AutomationId = "NavigateButton" + }; + + button.Clicked += async (s, e) => + { + await Shell.Current.GoToAsync("NewPage1"); + }; + + Content = new VerticalStackLayout + { + Padding = 20, + Children = + { + new Label { Text = "Welcome to .NET MAUI!" }, + button + } + }; + } +} + +public class Issue32278NewPage : ContentPage +{ + public Issue32278NewPage() + { + Title = "NewPage1"; + + // Add a label at the top to help verify that content is positioned correctly + // If SafeAreaEdges is not working, content will be clipped under the toolbar + var topLabel = new Label + { + Text = "Top Label - Should be visible", + AutomationId = "TopLabel", + BackgroundColor = Colors.Yellow, + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Start + }; + + var contentLabel = new Label + { + Text = "Welcome to .NET MAUI!", + AutomationId = "ContentLabel", + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center + }; + + Content = new Grid + { + BackgroundColor = Colors.White, + Children = + { + new VerticalStackLayout + { + Spacing = 10, + Children = + { + topLabel, + contentLabel + } + } + } + }; + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs new file mode 100644 index 000000000000..ec15b42d4183 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue32278 : _IssuesUITest + { + public Issue32278(TestDevice device) : base(device) { } + + public override string Issue => "Shell content page title position incorrect/clipped"; + + [Test] + [Category(UITestCategories.Shell)] + public void ShellNavigationPageTitleNotClipped() + { + // Wait for the main page to load + App.WaitForElement("NavigateButton"); + + // Navigate to the new page + App.Tap("NavigateButton"); + + // Wait for the new page to appear + App.WaitForElement("TopLabel"); + App.WaitForElement("ContentLabel"); + + // Verify the top label is visible (not clipped by toolbar) + var topLabel = App.FindElement("TopLabel"); + Assert.IsNotNull(topLabel, "Top label should be present"); + + // Get the bounding box of the top label + var rect = topLabel.GetRect(); + + // On Android API 28, the top label should be visible below the toolbar + // If SafeAreaEdges is working correctly, the Y position should be > 0 + // and the label should be visible (not positioned at negative Y or clipped) + Assert.That(rect.Y, Is.GreaterThan(0), + "Top label should be positioned below the toolbar, not clipped"); + + // Verify content label is also visible + var contentLabel = App.FindElement("ContentLabel"); + Assert.IsNotNull(contentLabel, "Content label should be present"); + + // Visual verification screenshot + VerifyScreenshot(); + } + } +} diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index 9763ec85ed32..9c91559662b3 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -211,8 +211,49 @@ public MauiWindowInsetListener() : base(DispatchModeStop) static WindowInsetsCompat? ApplyDefaultWindowInsets(AView v, WindowInsetsCompat insets) { - var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); - var displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); + // Get system bars insets with API level fallback + // WindowInsetsCompat.Type.SystemBars() was added in API 30 + // For API 28-29, we need to use the deprecated SystemWindowInsets property + Insets? systemBars; + Insets? displayCutout; + + if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.R) // API 30+ + { + systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); + displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); + } + else // API 28-29 + { + // Use legacy SystemWindowInsets for older APIs + // These values represent the area occupied by system bars +#pragma warning disable CS0618 // Type or member is obsolete + var legacyInsets = insets.SystemWindowInsets; +#pragma warning restore CS0618 + + systemBars = Insets.Of( + legacyInsets?.Left ?? 0, + legacyInsets?.Top ?? 0, + legacyInsets?.Right ?? 0, + legacyInsets?.Bottom ?? 0 + ); + + // DisplayCutout API was added in API 28, but getInsets(Type.DisplayCutout) is API 30+ + // For API 28-29, use DisplayCutout if available + var cutout = insets.DisplayCutout; + if (cutout != null) + { + displayCutout = Insets.Of( + cutout.SafeInsetLeft, + cutout.SafeInsetTop, + cutout.SafeInsetRight, + cutout.SafeInsetBottom + ); + } + else + { + displayCutout = Insets.None; + } + } // Handle MaterialToolbar special case early if (v is MaterialToolbar) From 203699b713db39355d969af9b9835eb63e464f45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 23:03:08 +0000 Subject: [PATCH 03/15] Use OperatingSystem.IsAndroidVersionAtLeast(30) for API version check Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com> --- src/Core/src/Platform/Android/MauiWindowInsetListener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index 9c91559662b3..db12af5d39d9 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -217,7 +217,7 @@ public MauiWindowInsetListener() : base(DispatchModeStop) Insets? systemBars; Insets? displayCutout; - if (global::Android.OS.Build.VERSION.SdkInt >= global::Android.OS.BuildVersionCodes.R) // API 30+ + if (OperatingSystem.IsAndroidVersionAtLeast(30)) { systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); From e50bfa0104146f1524b234ccc201c304946db420 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 12 Nov 2025 17:28:39 -0600 Subject: [PATCH 04/15] - fix tests --- .../Issues/Issue32278.xaml.cs | 51 ++++++------ .../Tests/Issues/Issue32278.cs | 82 +++++++++---------- 2 files changed, 64 insertions(+), 69 deletions(-) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs index 6f9ee20375e5..cb3ec4251bfd 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs @@ -6,7 +6,6 @@ public partial class Issue32278 : Shell public Issue32278() { InitializeComponent(); - Routing.RegisterRoute("NewPage1", typeof(Issue32278NewPage)); } } @@ -16,23 +15,34 @@ public Issue32278MainPage() { Title = "MainPage"; + // Add a top label on the main page to compare position with the second page + var topLabel = new Label + { + Text = "Top Label - Page 1", + AutomationId = "TopLabelPage1", + BackgroundColor = Colors.LightBlue, + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Start, + Padding = 10 + }; + var button = new Button { - Text = "Navigate to NewPage1", + Text = "Navigate to Page 2", AutomationId = "NavigateButton" }; button.Clicked += async (s, e) => { - await Shell.Current.GoToAsync("NewPage1"); + await Navigation.PushAsync(new Issue32278NewPage()); }; Content = new VerticalStackLayout { - Padding = 20, + Spacing = 10, Children = { - new Label { Text = "Welcome to .NET MAUI!" }, + topLabel, button } }; @@ -43,41 +53,26 @@ public class Issue32278NewPage : ContentPage { public Issue32278NewPage() { - Title = "NewPage1"; + Title = "Page 2"; // Add a label at the top to help verify that content is positioned correctly // If SafeAreaEdges is not working, content will be clipped under the toolbar var topLabel = new Label { - Text = "Top Label - Should be visible", - AutomationId = "TopLabel", + Text = "Top Label - Page 2", + AutomationId = "TopLabelPage2", BackgroundColor = Colors.Yellow, HorizontalOptions = LayoutOptions.Fill, - VerticalOptions = LayoutOptions.Start + VerticalOptions = LayoutOptions.Start, + Padding = 10 }; - var contentLabel = new Label - { - Text = "Welcome to .NET MAUI!", - AutomationId = "ContentLabel", - VerticalOptions = LayoutOptions.Center, - HorizontalOptions = LayoutOptions.Center - }; - - Content = new Grid + Content = new VerticalStackLayout { - BackgroundColor = Colors.White, + Spacing = 10, Children = { - new VerticalStackLayout - { - Spacing = 10, - Children = - { - topLabel, - contentLabel - } - } + topLabel } }; } diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs index ec15b42d4183..a072643fba0b 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs @@ -4,45 +4,45 @@ namespace Microsoft.Maui.TestCases.Tests.Issues { - public class Issue32278 : _IssuesUITest - { - public Issue32278(TestDevice device) : base(device) { } - - public override string Issue => "Shell content page title position incorrect/clipped"; - - [Test] - [Category(UITestCategories.Shell)] - public void ShellNavigationPageTitleNotClipped() - { - // Wait for the main page to load - App.WaitForElement("NavigateButton"); - - // Navigate to the new page - App.Tap("NavigateButton"); - - // Wait for the new page to appear - App.WaitForElement("TopLabel"); - App.WaitForElement("ContentLabel"); - - // Verify the top label is visible (not clipped by toolbar) - var topLabel = App.FindElement("TopLabel"); - Assert.IsNotNull(topLabel, "Top label should be present"); - - // Get the bounding box of the top label - var rect = topLabel.GetRect(); - - // On Android API 28, the top label should be visible below the toolbar - // If SafeAreaEdges is working correctly, the Y position should be > 0 - // and the label should be visible (not positioned at negative Y or clipped) - Assert.That(rect.Y, Is.GreaterThan(0), - "Top label should be positioned below the toolbar, not clipped"); - - // Verify content label is also visible - var contentLabel = App.FindElement("ContentLabel"); - Assert.IsNotNull(contentLabel, "Content label should be present"); - - // Visual verification screenshot - VerifyScreenshot(); - } - } +public class Issue32278 : _IssuesUITest +{ +public Issue32278(TestDevice device) : base(device) { } + +public override string Issue => "Shell content page title position incorrect/clipped"; + +[Test] +[Category(UITestCategories.Shell)] +public void ShellNavigationPageTitleNotClipped() +{ +// Wait for the main page to load +App.WaitForElement("NavigateButton"); +App.WaitForElement("TopLabelPage1"); + +// Get the position of the top label on Page 1 +var topLabelPage1 = App.FindElement("TopLabelPage1"); +var rectPage1 = topLabelPage1.GetRect(); + +// Navigate to the new page +App.Tap("NavigateButton"); + +// Wait for the second page to appear +App.WaitForElement("TopLabelPage2"); + +// Get the position of the top label on Page 2 +var topLabelPage2 = App.FindElement("TopLabelPage2"); +var rectPage2 = topLabelPage2.GetRect(); + +// The top labels should be at the same Y position on both pages +// If SafeAreaEdges is broken on the second page, the label will be clipped under the toolbar +// and positioned differently than on the first page +Assert.That(rectPage2.Y, Is.EqualTo(rectPage1.Y).Within(5), +$"Top label on Page 2 (Y={rectPage2.Y}) should be at the same position as Page 1 (Y={rectPage1.Y})"); + +// Both labels should be below the toolbar (not at Y=0 or negative) +Assert.That(rectPage1.Y, Is.GreaterThan(0), +$"Top label on Page 1 should be below the toolbar (Y={rectPage1.Y})"); +Assert.That(rectPage2.Y, Is.GreaterThan(0), +$"Top label on Page 2 should be below the toolbar (Y={rectPage2.Y})"); +} +} } From 7043580e4241c12e47f522d5b7c742009d556664 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 13 Nov 2025 09:42:06 -0600 Subject: [PATCH 05/15] - container --- .../Shell/Android/ShellContentFragment.cs | 2 +- .../Shell/Android/ShellPageContainer.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs index ad0f11feeada..57ad6eea761f 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs @@ -149,7 +149,7 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, appBar.AddView(_toolbar); _viewhandler = _page.ToHandler(shellContentMauiContext); - _shellPageContainer = new ShellPageContainer(Context, _viewhandler); + _shellPageContainer = new BoopContainer(Context, _viewhandler); if (_root is ViewGroup vg) vg.AddView(_shellPageContainer); diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs index c58fcf794e9f..4970d6cef245 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs @@ -10,6 +10,18 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility { + internal class BoopContainer : ShellPageContainer + { + public BoopContainer(Context context, IPlatformViewHandler child, bool inFragment = false) : base(context, child, inFragment) + { + } + + + public override WindowInsets OnApplyWindowInsets(WindowInsets insets) + { + return base.OnApplyWindowInsets(insets); + } + } internal class ShellPageContainer : ViewGroup { static int? DarkBackground; @@ -57,5 +69,10 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) else SetMeasuredDimension(0, 0); } + + public override WindowInsets OnApplyWindowInsets(WindowInsets insets) + { + return base.OnApplyWindowInsets(insets); + } } } From 60f607e06cc2b14d4357a053735c8684c3eb8fab Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 13 Nov 2025 17:17:32 -0600 Subject: [PATCH 06/15] - Apply API 28 Workaround --- .../Shell/Android/ShellItemRenderer.cs | 57 ++++++++++++++ .../Android/MauiWindowInsetListener.cs | 75 ++++++++++++++++--- 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs index e75fdf86a5ce..433ac53b8685 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs @@ -8,6 +8,7 @@ using Android.OS; using Android.Views; using Android.Widget; +using AndroidX.Core.View; using Google.Android.Material.BottomNavigation; using Google.Android.Material.BottomSheet; using Google.Android.Material.Navigation; @@ -76,6 +77,9 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, _navigationArea = PlatformInterop.CreateNavigationBarArea(context, _outerLayout); _bottomView = PlatformInterop.CreateNavigationBar(context, Resource.Attribute.bottomNavigationViewStyle, _outerLayout, this); + // Add inset listener for API 28-29 workaround + SetupNavigationAreaInsetListener(); + if (ShellItem is null) throw new InvalidOperationException("Active Shell Item not set. Have you added any Shell Items to your Shell?"); @@ -92,6 +96,17 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, return _outerLayout; } + void SetupNavigationAreaInsetListener() + { + // Only set up listener for API 28-29 + if ((int)global::Android.OS.Build.VERSION.SdkInt >= 30) + return; + + if (_navigationArea == null) + return; + + ViewCompat.SetOnApplyWindowInsetsListener(_navigationArea, new NavigationAreaInsetListener()); + } void Destroy() { @@ -523,5 +538,47 @@ protected virtual void UpdateTabBarVisibility() SetAppearance(_shellAppearance); } } + + private class NavigationAreaInsetListener : Java.Lang.Object, IOnApplyWindowInsetsListener + { + public WindowInsetsCompat OnApplyWindowInsets(AView v, WindowInsetsCompat insets) + { + if (insets == null) + return insets; + + // Convert to platform WindowInsets for dispatching + var platformInsets = insets.ToWindowInsets(); + if (platformInsets == null) + return insets; + + // Apply the Google workaround: dispatch to first-level children only + // This fixes the API 28-29 bug where one child consuming insets blocks siblings + // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 + // Note: Only dispatches to immediate children; deeper propagation handled by MauiWindowInsetListener + DispatchToFirstLevelChildren(v as ViewGroup, platformInsets); + + return insets; + } + + // Dispatches to first-level children only (non-recursive) + // Deep propagation is handled by MauiWindowInsetListener for API < 30 + private void DispatchToFirstLevelChildren(ViewGroup parent, WindowInsets insets) + { + if (parent == null || insets == null) + return; + + int childCount = parent.ChildCount; + + for (int i = 0; i < childCount; i++) + { + var child = parent.GetChildAt(i); + if (child != null) + { + // Dispatch to immediate child only (non-recursive) + child.DispatchApplyWindowInsets(insets); + } + } + } + } } } \ No newline at end of file diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index db12af5d39d9..3c6ca54acd7a 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -3,6 +3,7 @@ using System.Linq; using Android.Content; using Android.Views; +using AndroidX.CoordinatorLayout.Widget; using AndroidX.Core.Graphics; using AndroidX.Core.View; using AndroidX.Core.Widget; @@ -202,11 +203,67 @@ public MauiWindowInsetListener() : base(DispatchModeStop) // Handle custom inset views first if (v is IHandleWindowInsets customHandler) { - return customHandler.HandleWindowInsets(v, insets); + var result = customHandler.HandleWindowInsets(v, insets); + return result; } // Apply default window insets for standard views - return ApplyDefaultWindowInsets(v, insets); + var returnValue = ApplyDefaultWindowInsets(v, insets); + + // For API < 30 and CoordinatorLayout only, apply Google's workaround to dispatch to all children + if ((int)global::Android.OS.Build.VERSION.SdkInt < 30 && v is CoordinatorLayout coordinatorLayout) + { + // Convert back to platform WindowInsets for propagation + var platformInsets = returnValue?.ToWindowInsets(); + if (platformInsets != null) + { + // Start recursive dispatch using the result, not the original insets + DispatchInsetsToAllChildren(coordinatorLayout, platformInsets); + } + + return WindowInsetsCompat.Consumed; + } + + return returnValue; + } + + // Dispatches insets to all children recursively (for API < 30) + // This implements Google's workaround for the API 28-29 bug where + // one child consuming insets blocks all siblings from receiving them. + // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 + static WindowInsets? DispatchInsetsToAllChildren(AView view, WindowInsets insets) + { + if (view == null || insets == null) + { + return insets; + } + + // Skip propagation for MauiScrollView and IMauiRecyclerView + if (view is MauiScrollView || view is IMauiRecyclerView) + return insets; + + // First, let the view's default handler process the insets + var outInsets = view.OnApplyWindowInsets(insets); + + // Only propagate to children if: + // 1. outInsets is not null + // 2. outInsets is not consumed + // 3. view is a ViewGroup + // 4. view is not MaterialToolbar (propagate TO toolbar, but not to its children) + if (outInsets != null && !outInsets.IsConsumed && view is ViewGroup parent && view is not MaterialToolbar) + { + for (int i = 0; i < parent.ChildCount; i++) + { + var child = parent.GetChildAt(i); + if (child != null) + { + // Recursively dispatch the RESULT insets (outInsets), not the original + DispatchInsetsToAllChildren(child, outInsets); + } + } + } + + return outInsets; } static WindowInsetsCompat? ApplyDefaultWindowInsets(AView v, WindowInsetsCompat insets) @@ -216,7 +273,7 @@ public MauiWindowInsetListener() : base(DispatchModeStop) // For API 28-29, we need to use the deprecated SystemWindowInsets property Insets? systemBars; Insets? displayCutout; - + if (OperatingSystem.IsAndroidVersionAtLeast(30)) { systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); @@ -229,14 +286,14 @@ public MauiWindowInsetListener() : base(DispatchModeStop) #pragma warning disable CS0618 // Type or member is obsolete var legacyInsets = insets.SystemWindowInsets; #pragma warning restore CS0618 - + systemBars = Insets.Of( legacyInsets?.Left ?? 0, legacyInsets?.Top ?? 0, legacyInsets?.Right ?? 0, legacyInsets?.Bottom ?? 0 ); - + // DisplayCutout API was added in API 28, but getInsets(Type.DisplayCutout) is API 30+ // For API 28-29, use DisplayCutout if available var cutout = insets.DisplayCutout; @@ -345,10 +402,10 @@ public void TrackView(AView view) public bool HasTrackedView => _trackedViews.Count > 0; - public bool IsViewTracked(AView view) - { - return _trackedViews.Contains(view); - } + public bool IsViewTracked(AView view) + { + return _trackedViews.Contains(view); + } public void ResetView(AView view) { if (view is IHandleWindowInsets customHandler) From fb5f67d1aee5269f6f861171c500a3c5a615acab Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 13 Nov 2025 18:18:30 -0600 Subject: [PATCH 07/15] - switched implementation to google compat method --- .../Shell/Android/ShellItemRenderer.cs | 15 ----- .../Android/MauiWindowInsetListener.cs | 58 +------------------ .../Navigation/NavigationRootManager.cs | 12 +++- 3 files changed, 12 insertions(+), 73 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs index 433ac53b8685..9ec5738a80de 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs @@ -77,9 +77,6 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, _navigationArea = PlatformInterop.CreateNavigationBarArea(context, _outerLayout); _bottomView = PlatformInterop.CreateNavigationBar(context, Resource.Attribute.bottomNavigationViewStyle, _outerLayout, this); - // Add inset listener for API 28-29 workaround - SetupNavigationAreaInsetListener(); - if (ShellItem is null) throw new InvalidOperationException("Active Shell Item not set. Have you added any Shell Items to your Shell?"); @@ -96,18 +93,6 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, return _outerLayout; } - void SetupNavigationAreaInsetListener() - { - // Only set up listener for API 28-29 - if ((int)global::Android.OS.Build.VERSION.SdkInt >= 30) - return; - - if (_navigationArea == null) - return; - - ViewCompat.SetOnApplyWindowInsetsListener(_navigationArea, new NavigationAreaInsetListener()); - } - void Destroy() { if (ShellItem is not null) diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index 3c6ca54acd7a..669de708cb5e 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -207,63 +207,7 @@ public MauiWindowInsetListener() : base(DispatchModeStop) return result; } - // Apply default window insets for standard views - var returnValue = ApplyDefaultWindowInsets(v, insets); - - // For API < 30 and CoordinatorLayout only, apply Google's workaround to dispatch to all children - if ((int)global::Android.OS.Build.VERSION.SdkInt < 30 && v is CoordinatorLayout coordinatorLayout) - { - // Convert back to platform WindowInsets for propagation - var platformInsets = returnValue?.ToWindowInsets(); - if (platformInsets != null) - { - // Start recursive dispatch using the result, not the original insets - DispatchInsetsToAllChildren(coordinatorLayout, platformInsets); - } - - return WindowInsetsCompat.Consumed; - } - - return returnValue; - } - - // Dispatches insets to all children recursively (for API < 30) - // This implements Google's workaround for the API 28-29 bug where - // one child consuming insets blocks all siblings from receiving them. - // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 - static WindowInsets? DispatchInsetsToAllChildren(AView view, WindowInsets insets) - { - if (view == null || insets == null) - { - return insets; - } - - // Skip propagation for MauiScrollView and IMauiRecyclerView - if (view is MauiScrollView || view is IMauiRecyclerView) - return insets; - - // First, let the view's default handler process the insets - var outInsets = view.OnApplyWindowInsets(insets); - - // Only propagate to children if: - // 1. outInsets is not null - // 2. outInsets is not consumed - // 3. view is a ViewGroup - // 4. view is not MaterialToolbar (propagate TO toolbar, but not to its children) - if (outInsets != null && !outInsets.IsConsumed && view is ViewGroup parent && view is not MaterialToolbar) - { - for (int i = 0; i < parent.ChildCount; i++) - { - var child = parent.GetChildAt(i); - if (child != null) - { - // Recursively dispatch the RESULT insets (outInsets), not the original - DispatchInsetsToAllChildren(child, outInsets); - } - } - } - - return outInsets; + return ApplyDefaultWindowInsets(v, insets); } static WindowInsetsCompat? ApplyDefaultWindowInsets(AView v, WindowInsetsCompat insets) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index a8fa541dc466..aa47cfa8562e 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -5,6 +5,7 @@ using Android.Views; using AndroidX.AppCompat.Widget; using AndroidX.CoordinatorLayout.Widget; +using AndroidX.Core.View; using AndroidX.DrawerLayout.Widget; using AndroidX.Fragment.App; using Google.Android.Material.AppBar; @@ -83,7 +84,16 @@ internal void Connect(IView? view, IMauiContext? mauiContext = null) } _rootView = navigationLayout; - } + } + + if(!OperatingSystem.IsAndroidVersionAtLeast(30)) + { + // Dispatches insets to all children recursively (for API < 30) + // This implements Google's workaround for the API 28-29 bug where + // one child consuming insets blocks all siblings from receiving them. + // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 + ViewGroupCompat.InstallCompatInsetsDispatch(_rootView); + } // if the incoming view is a Drawer Layout then the Drawer Layout // will be the root view and internally handle all if its view management From 74b544745429b2ba2d4b04621f79b5ca6ba26830 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 00:27:38 +0000 Subject: [PATCH 08/15] Revert changes to ShellContentFragment, ShellItemRenderer, and ShellPageContainer Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../Shell/Android/ShellContentFragment.cs | 2 +- .../Shell/Android/ShellItemRenderer.cs | 44 +------------------ .../Shell/Android/ShellPageContainer.cs | 17 ------- .../Android/MauiWindowInsetListener.cs | 3 +- 4 files changed, 3 insertions(+), 63 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs index 57ad6eea761f..ad0f11feeada 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs @@ -149,7 +149,7 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, appBar.AddView(_toolbar); _viewhandler = _page.ToHandler(shellContentMauiContext); - _shellPageContainer = new BoopContainer(Context, _viewhandler); + _shellPageContainer = new ShellPageContainer(Context, _viewhandler); if (_root is ViewGroup vg) vg.AddView(_shellPageContainer); diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs index 9ec5738a80de..e75fdf86a5ce 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs @@ -8,7 +8,6 @@ using Android.OS; using Android.Views; using Android.Widget; -using AndroidX.Core.View; using Google.Android.Material.BottomNavigation; using Google.Android.Material.BottomSheet; using Google.Android.Material.Navigation; @@ -93,6 +92,7 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, return _outerLayout; } + void Destroy() { if (ShellItem is not null) @@ -523,47 +523,5 @@ protected virtual void UpdateTabBarVisibility() SetAppearance(_shellAppearance); } } - - private class NavigationAreaInsetListener : Java.Lang.Object, IOnApplyWindowInsetsListener - { - public WindowInsetsCompat OnApplyWindowInsets(AView v, WindowInsetsCompat insets) - { - if (insets == null) - return insets; - - // Convert to platform WindowInsets for dispatching - var platformInsets = insets.ToWindowInsets(); - if (platformInsets == null) - return insets; - - // Apply the Google workaround: dispatch to first-level children only - // This fixes the API 28-29 bug where one child consuming insets blocks siblings - // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 - // Note: Only dispatches to immediate children; deeper propagation handled by MauiWindowInsetListener - DispatchToFirstLevelChildren(v as ViewGroup, platformInsets); - - return insets; - } - - // Dispatches to first-level children only (non-recursive) - // Deep propagation is handled by MauiWindowInsetListener for API < 30 - private void DispatchToFirstLevelChildren(ViewGroup parent, WindowInsets insets) - { - if (parent == null || insets == null) - return; - - int childCount = parent.ChildCount; - - for (int i = 0; i < childCount; i++) - { - var child = parent.GetChildAt(i); - if (child != null) - { - // Dispatch to immediate child only (non-recursive) - child.DispatchApplyWindowInsets(insets); - } - } - } - } } } \ No newline at end of file diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs index 4970d6cef245..c58fcf794e9f 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs @@ -10,18 +10,6 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility { - internal class BoopContainer : ShellPageContainer - { - public BoopContainer(Context context, IPlatformViewHandler child, bool inFragment = false) : base(context, child, inFragment) - { - } - - - public override WindowInsets OnApplyWindowInsets(WindowInsets insets) - { - return base.OnApplyWindowInsets(insets); - } - } internal class ShellPageContainer : ViewGroup { static int? DarkBackground; @@ -69,10 +57,5 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) else SetMeasuredDimension(0, 0); } - - public override WindowInsets OnApplyWindowInsets(WindowInsets insets) - { - return base.OnApplyWindowInsets(insets); - } } } diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index 669de708cb5e..e5b263fa0942 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -203,8 +203,7 @@ public MauiWindowInsetListener() : base(DispatchModeStop) // Handle custom inset views first if (v is IHandleWindowInsets customHandler) { - var result = customHandler.HandleWindowInsets(v, insets); - return result; + return customHandler.HandleWindowInsets(v, insets); } return ApplyDefaultWindowInsets(v, insets); From fe1649fafceb76cdb5fd4701d17e5be98ff0f6a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 00:54:31 +0000 Subject: [PATCH 09/15] Revert changes to common-testing-patterns.md and MauiWindowInsetListener.cs Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../instructions/common-testing-patterns.md | 51 ----------------- .../Android/MauiWindowInsetListener.cs | 55 +++---------------- 2 files changed, 7 insertions(+), 99 deletions(-) diff --git a/.github/instructions/common-testing-patterns.md b/.github/instructions/common-testing-patterns.md index 1aeb8f26a80e..8c7c7aa0c3ad 100644 --- a/.github/instructions/common-testing-patterns.md +++ b/.github/instructions/common-testing-patterns.md @@ -106,57 +106,6 @@ echo "Simulator is booted and ready" --- -### Android Emulator Startup with Error Checking - -**Used in**: Investigation work - -**Pattern**: -```bash -# Clean up any existing emulator processes -pkill -9 qemu-system-x86_64 2>/dev/null || true -pkill -9 emulator 2>/dev/null || true -sleep 2 - -# Restart adb server -adb kill-server && sleep 1 && adb start-server && sleep 2 - -# Start emulator (must run from SDK emulator directory to find dependencies) -cd $ANDROID_HOME/emulator && ./emulator -avd Nexus_5X_API_30 -no-snapshot-load -no-audio -no-boot-anim & - -# Wait for device to appear -echo "Waiting for device to appear..." -adb wait-for-device - -# Wait for boot to complete -echo "Waiting for boot to complete..." -until [ "$(adb shell getprop sys.boot_completed 2>/dev/null)" = "1" ]; do - sleep 2 - echo -n "." -done -echo "" - -# Get device UDID -export DEVICE_UDID=$(adb devices | grep -v "List" | grep "device" | awk '{print $1}' | head -1) - -# Verify device is ready -if [ -z "$DEVICE_UDID" ]; then - echo "❌ ERROR: Emulator started but device not found" - exit 1 -fi - -# Check API level -API_LEVEL=$(adb shell getprop ro.build.version.sdk) -echo "✅ Emulator ready: $DEVICE_UDID (API $API_LEVEL)" -``` - -**When to use**: Starting Android emulator for testing - -**Critical detail**: The emulator command must be run from `$ANDROID_HOME/emulator` directory. Running from other directories causes "Qt library not found" and "qemu-system not found" errors. - -**Available emulators**: List with `emulator -list-avds` - ---- - ## 3. Build Patterns ### Sandbox App Build (iOS) diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs index e5b263fa0942..9763ec85ed32 100644 --- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs +++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs @@ -3,7 +3,6 @@ using System.Linq; using Android.Content; using Android.Views; -using AndroidX.CoordinatorLayout.Widget; using AndroidX.Core.Graphics; using AndroidX.Core.View; using AndroidX.Core.Widget; @@ -206,54 +205,14 @@ public MauiWindowInsetListener() : base(DispatchModeStop) return customHandler.HandleWindowInsets(v, insets); } + // Apply default window insets for standard views return ApplyDefaultWindowInsets(v, insets); } static WindowInsetsCompat? ApplyDefaultWindowInsets(AView v, WindowInsetsCompat insets) { - // Get system bars insets with API level fallback - // WindowInsetsCompat.Type.SystemBars() was added in API 30 - // For API 28-29, we need to use the deprecated SystemWindowInsets property - Insets? systemBars; - Insets? displayCutout; - - if (OperatingSystem.IsAndroidVersionAtLeast(30)) - { - systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); - displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); - } - else // API 28-29 - { - // Use legacy SystemWindowInsets for older APIs - // These values represent the area occupied by system bars -#pragma warning disable CS0618 // Type or member is obsolete - var legacyInsets = insets.SystemWindowInsets; -#pragma warning restore CS0618 - - systemBars = Insets.Of( - legacyInsets?.Left ?? 0, - legacyInsets?.Top ?? 0, - legacyInsets?.Right ?? 0, - legacyInsets?.Bottom ?? 0 - ); - - // DisplayCutout API was added in API 28, but getInsets(Type.DisplayCutout) is API 30+ - // For API 28-29, use DisplayCutout if available - var cutout = insets.DisplayCutout; - if (cutout != null) - { - displayCutout = Insets.Of( - cutout.SafeInsetLeft, - cutout.SafeInsetTop, - cutout.SafeInsetRight, - cutout.SafeInsetBottom - ); - } - else - { - displayCutout = Insets.None; - } - } + var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()); + var displayCutout = insets.GetInsets(WindowInsetsCompat.Type.DisplayCutout()); // Handle MaterialToolbar special case early if (v is MaterialToolbar) @@ -345,10 +304,10 @@ public void TrackView(AView view) public bool HasTrackedView => _trackedViews.Count > 0; - public bool IsViewTracked(AView view) - { - return _trackedViews.Contains(view); - } + public bool IsViewTracked(AView view) + { + return _trackedViews.Contains(view); + } public void ResetView(AView view) { if (view is IHandleWindowInsets customHandler) From ab9d422cd152969af33a62400a284e3613ad2055 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 00:59:41 +0000 Subject: [PATCH 10/15] Revert changes to NavigationRootManager.cs Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../Android/Navigation/NavigationRootManager.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index aa47cfa8562e..a8fa541dc466 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -5,7 +5,6 @@ using Android.Views; using AndroidX.AppCompat.Widget; using AndroidX.CoordinatorLayout.Widget; -using AndroidX.Core.View; using AndroidX.DrawerLayout.Widget; using AndroidX.Fragment.App; using Google.Android.Material.AppBar; @@ -84,16 +83,7 @@ internal void Connect(IView? view, IMauiContext? mauiContext = null) } _rootView = navigationLayout; - } - - if(!OperatingSystem.IsAndroidVersionAtLeast(30)) - { - // Dispatches insets to all children recursively (for API < 30) - // This implements Google's workaround for the API 28-29 bug where - // one child consuming insets blocks all siblings from receiving them. - // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 - ViewGroupCompat.InstallCompatInsetsDispatch(_rootView); - } + } // if the incoming view is a Drawer Layout then the Drawer Layout // will be the root view and internally handle all if its view management From ee8766b836d95d0062c9ff6b0cac16d4ec086834 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:22:32 +0000 Subject: [PATCH 11/15] Revert "Revert changes to NavigationRootManager.cs" This reverts commit c1fd500435e845a780b67b2659d2fdbc971da852. --- .../Android/Navigation/NavigationRootManager.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index a8fa541dc466..aa47cfa8562e 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -5,6 +5,7 @@ using Android.Views; using AndroidX.AppCompat.Widget; using AndroidX.CoordinatorLayout.Widget; +using AndroidX.Core.View; using AndroidX.DrawerLayout.Widget; using AndroidX.Fragment.App; using Google.Android.Material.AppBar; @@ -83,7 +84,16 @@ internal void Connect(IView? view, IMauiContext? mauiContext = null) } _rootView = navigationLayout; - } + } + + if(!OperatingSystem.IsAndroidVersionAtLeast(30)) + { + // Dispatches insets to all children recursively (for API < 30) + // This implements Google's workaround for the API 28-29 bug where + // one child consuming insets blocks all siblings from receiving them. + // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 + ViewGroupCompat.InstallCompatInsetsDispatch(_rootView); + } // if the incoming view is a Drawer Layout then the Drawer Layout // will be the root view and internally handle all if its view management From c630d2e7eea2cef6288feaac9a3ad432b3a987a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:07:25 +0000 Subject: [PATCH 12/15] Add defensive logging and validation for null _rootView Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../Android/Navigation/NavigationRootManager.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index aa47cfa8562e..4499487e49ca 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -92,7 +92,16 @@ internal void Connect(IView? view, IMauiContext? mauiContext = null) // This implements Google's workaround for the API 28-29 bug where // one child consuming insets blocks all siblings from receiving them. // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 - ViewGroupCompat.InstallCompatInsetsDispatch(_rootView); + if (_rootView is null) + { + Microsoft.Maui.ApplicationModel.Logging.Logger.LogWarning( + "NavigationRootManager: _rootView is null when attempting to install compat insets dispatch. " + + "This may cause incorrect window insets behavior on API < 30."); + } + else + { + ViewGroupCompat.InstallCompatInsetsDispatch(_rootView); + } } // if the incoming view is a Drawer Layout then the Drawer Layout From ce4d72733cb415f4b666ac7d8b5521277bd91182 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:20:40 +0000 Subject: [PATCH 13/15] Rename test files from Issue32278 to Issue32526 to match GitHub issue number Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../Issues/{Issue32278.xaml => Issue32526.xaml} | 6 +++--- .../{Issue32278.xaml.cs => Issue32526.xaml.cs} | 16 ++++++++-------- .../Issues/{Issue32278.cs => Issue32526.cs} | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) rename src/Controls/tests/TestCases.HostApp/Issues/{Issue32278.xaml => Issue32526.xaml} (60%) rename src/Controls/tests/TestCases.HostApp/Issues/{Issue32278.xaml.cs => Issue32526.xaml.cs} (79%) rename src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/{Issue32278.cs => Issue32526.cs} (94%) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml similarity index 60% rename from src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml rename to src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml index 55eaf0af3e81..ade438ea19fa 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml @@ -2,9 +2,9 @@ + x:Class="Maui.Controls.Sample.Issues.Issue32526" + Title="Issue32526"> - + diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml.cs similarity index 79% rename from src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs rename to src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml.cs index cb3ec4251bfd..7386a9d3661b 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue32278.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32526.xaml.cs @@ -1,17 +1,17 @@ namespace Maui.Controls.Sample.Issues; -[Issue(IssueTracker.Github, 32278, "Shell content page title position incorrect/clipped", PlatformAffected.Android)] -public partial class Issue32278 : Shell +[Issue(IssueTracker.Github, 32526, "Shell content page title position incorrect/clipped", PlatformAffected.Android)] +public partial class Issue32526 : Shell { - public Issue32278() + public Issue32526() { InitializeComponent(); } } -public class Issue32278MainPage : ContentPage +public class Issue32526MainPage : ContentPage { - public Issue32278MainPage() + public Issue32526MainPage() { Title = "MainPage"; @@ -34,7 +34,7 @@ public Issue32278MainPage() button.Clicked += async (s, e) => { - await Navigation.PushAsync(new Issue32278NewPage()); + await Navigation.PushAsync(new Issue32526NewPage()); }; Content = new VerticalStackLayout @@ -49,9 +49,9 @@ public Issue32278MainPage() } } -public class Issue32278NewPage : ContentPage +public class Issue32526NewPage : ContentPage { - public Issue32278NewPage() + public Issue32526NewPage() { Title = "Page 2"; diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32526.cs similarity index 94% rename from src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs rename to src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32526.cs index a072643fba0b..2f6a75eefd69 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32278.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32526.cs @@ -4,9 +4,9 @@ namespace Microsoft.Maui.TestCases.Tests.Issues { -public class Issue32278 : _IssuesUITest +public class Issue32526 : _IssuesUITest { -public Issue32278(TestDevice device) : base(device) { } +public Issue32526(TestDevice device) : base(device) { } public override string Issue => "Shell content page title position incorrect/clipped"; From cae53453b290836354f110ddf5e5ad2805cfb419 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:13:33 +0000 Subject: [PATCH 14/15] Fix compilation error: use CreateLogger instead of non-existent Logging.Logger Co-authored-by: sheiksyedm <23059975+sheiksyedm@users.noreply.github.com> --- .../src/Platform/Android/Navigation/NavigationRootManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index 4499487e49ca..f00aa2264e40 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -94,7 +94,7 @@ internal void Connect(IView? view, IMauiContext? mauiContext = null) // Based on: https://android-review.googlesource.com/c/platform/frameworks/support/+/3310617 if (_rootView is null) { - Microsoft.Maui.ApplicationModel.Logging.Logger.LogWarning( + _mauiContext?.CreateLogger()?.LogWarning( "NavigationRootManager: _rootView is null when attempting to install compat insets dispatch. " + "This may cause incorrect window insets behavior on API < 30."); } From 5d7a7514c8b56846a737192601e67993b4ee5cd5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:03:38 +0000 Subject: [PATCH 15/15] Add Microsoft.Extensions.Logging using directive for LogWarning extension method Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com> --- .../src/Platform/Android/Navigation/NavigationRootManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs index f00aa2264e40..712d24da5208 100644 --- a/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs +++ b/src/Core/src/Platform/Android/Navigation/NavigationRootManager.cs @@ -9,6 +9,7 @@ using AndroidX.DrawerLayout.Widget; using AndroidX.Fragment.App; using Google.Android.Material.AppBar; +using Microsoft.Extensions.Logging; using AView = Android.Views.View; namespace Microsoft.Maui.Platform