diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986.cs index 262d38aa3a62..82f85f3e920d 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986.cs @@ -97,7 +97,6 @@ public void SafeAreaMainGridSequentialButtonTesting() Assert.That(finalAllPosition.Y, Is.EqualTo(allPosition.Y), "Final All position should match initial All position"); } -#if TEST_FAILS_ON_ANDROID [Test] [Category(UITestCategories.SafeAreaEdges)] public void SafeAreaPerEdgeValidation() @@ -128,6 +127,5 @@ public void SafeAreaPerEdgeValidation() Assert.That(containerPositionWithoutSoftInput.Height, Is.EqualTo(containerPosition.Height), "ContentGrid height should return to original when Soft Input is dismissed with Container edges"); }); } - #endif } #endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_ContentPage.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_ContentPage.cs index 98f83a954045..1941f6eb30fe 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_ContentPage.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_ContentPage.cs @@ -7,13 +7,13 @@ namespace Microsoft.Maui.TestCases.Tests.Issues; public class Issue28986_ContentPage : _IssuesUITest { - public override string Issue => "Test SafeArea ContentPage for per-edge safe area control"; + public override string Issue => "Test SafeArea ContentPage for per-edge safe area control"; - public Issue28986_ContentPage(TestDevice device) : base(device) - { - } + public Issue28986_ContentPage(TestDevice device) : base(device) + { + } - [Test] + [Test] [Category(UITestCategories.SafeAreaEdges)] public void SafeAreaMainGridBasicFunctionality() { @@ -97,8 +97,6 @@ public void SafeAreaMainGridSequentialButtonTesting() Assert.That(finalAllPosition.Y, Is.EqualTo(allPosition.Y), "Final All position should match initial All position"); } - - #if TEST_FAILS_ON_ANDROID [Test] [Category(UITestCategories.SafeAreaEdges)] public void SafeAreaPerEdgeValidation() @@ -129,6 +127,5 @@ public void SafeAreaPerEdgeValidation() Assert.That(containerPositionWithoutSoftInput.Height, Is.EqualTo(containerPosition.Height), "ContentGrid height should return to original when Soft Input is dismissed with Container edges"); }); } - #endif } #endif diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_SafeAreaBorderOrientation.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_SafeAreaBorderOrientation.cs index fc25da008faf..ca881500d8ec 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_SafeAreaBorderOrientation.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_SafeAreaBorderOrientation.cs @@ -224,7 +224,6 @@ public void SafeAreaBorderMultipleOrientationChanges() "Dimensions should still differ between portrait and landscape after multiple changes"); } -#if TEST_FAILS_ON_ANDROID [Test] [Category(UITestCategories.SafeAreaEdges)] public void SafeAreaBorderSoftInputBehavior() @@ -320,116 +319,5 @@ public void SafeAreaBorderSoftInputBehavior() Assert.That(borderHeightChanged || safeAreaChanged, Is.True, "Test should demonstrate that keyboard interaction affects either safe area or border layout"); } -#endif - -#if TEST_FAILS_ON_ANDROID // Landscape orientation causes keyboard to occupy fullview - [Test] - [Category(UITestCategories.SafeAreaEdges)] - public void SafeAreaBorderSoftInputWithOrientationChange() - { - var borderContent = App.WaitForElement("BorderContent"); - - // 1. Set bottom edge to SoftInput mode for keyboard behavior - App.Tap("SetBottomSoftInputButton"); - - // Wait for the test status to confirm the change - var softInputSet = App.WaitForTextToBePresentInElement("TestStatus", "SoftInput", TimeSpan.FromSeconds(3)); - Assert.That(softInputSet, Is.True, "Bottom edge should be set to SoftInput mode"); - - // 2. Show keyboard in portrait mode - App.Tap("TestEntry"); - - Thread.Sleep(1000); - - Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should become visible in portrait mode"); - - // 3. Record portrait state with keyboard visible - var portraitWithKeyboardBounds = borderContent.GetRect(); - var portraitWithKeyboardSafeArea = App.WaitForElement("SafeAreaInsets").GetText(); - - Assert.That(App.IsKeyboardShown(), Is.True, "IsKeyboardShown should confirm keyboard is visible"); - - // Verify we start in portrait with keyboard - Assert.That(portraitWithKeyboardBounds.Height, Is.GreaterThan(portraitWithKeyboardBounds.Width * 0.6), - "Should be in portrait orientation even with keyboard visible"); - - // 4. Change orientation to landscape while keyboard is still visible - App.SetOrientationLandscape(); - Thread.Sleep(2000); // Wait for orientation change and layout to settle - - // 5. Record landscape state with keyboard visible - var landscapeWithKeyboardBounds = borderContent.GetRect(); - var landscapeWithKeyboardSafeArea = App.WaitForElement("SafeAreaInsets").GetText(); - - Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should remain visible after orientation change"); - - // Verify orientation changed to landscape - Assert.That(landscapeWithKeyboardBounds.Width, Is.GreaterThan(landscapeWithKeyboardBounds.Height * 0.6), - "Should now be in landscape orientation with keyboard visible"); - - // 6. Verify border maintains visibility in landscape with keyboard - Assert.That(landscapeWithKeyboardBounds.Width, Is.GreaterThan(0), - "Border should remain visible in landscape with keyboard"); - Assert.That(landscapeWithKeyboardBounds.Height, Is.GreaterThan(0), - "Border should remain visible in landscape with keyboard"); - - // 7. Verify dimensions differ between orientations even with keyboard visible - Assert.That(portraitWithKeyboardBounds.Width, Is.Not.EqualTo(landscapeWithKeyboardBounds.Width).Within(10), - "Border width should differ between portrait and landscape with keyboard visible"); - Assert.That(portraitWithKeyboardBounds.Height, Is.Not.EqualTo(landscapeWithKeyboardBounds.Height).Within(10), - "Border height should differ between portrait and landscape with keyboard visible"); - - // 8. Verify landscape has wider aspect ratio even with keyboard - var portraitAspectRatio = portraitWithKeyboardBounds.Height / portraitWithKeyboardBounds.Width; - var landscapeAspectRatio = landscapeWithKeyboardBounds.Height / landscapeWithKeyboardBounds.Width; - - Assert.That(portraitAspectRatio, Is.GreaterThan(landscapeAspectRatio), - "Portrait should still have higher aspect ratio than landscape, even with keyboard visible"); - - // 9. Verify safe area insets adapted to landscape with keyboard - Assert.That(portraitWithKeyboardSafeArea, Is.Not.EqualTo(landscapeWithKeyboardSafeArea), - "Safe area insets should be different between portrait and landscape with keyboard"); - - // 10. Hide keyboard in landscape mode using proper dismiss method - App.DismissKeyboard(); - Thread.Sleep(1000); - - Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden in landscape mode"); - - // 11. Record landscape state with keyboard hidden - var landscapeWithoutKeyboardBounds = borderContent.GetRect(); - - Assert.That(App.IsKeyboardShown(), Is.False, "IsKeyboardShown should confirm keyboard is hidden"); - - // 12. Verify border expanded when keyboard was dismissed in landscape - Assert.That(landscapeWithoutKeyboardBounds.Height, Is.GreaterThan(landscapeWithKeyboardBounds.Height), - "Border should expand when keyboard is hidden in landscape mode"); - - // 13. Change back to portrait without keyboard - App.SetOrientationPortrait(); - Thread.Sleep(2000); // Wait for orientation change and layout to settle - - // 14. Record final portrait state without keyboard - var finalPortraitBounds = borderContent.GetRect(); - - // Verify back in portrait - Assert.That(finalPortraitBounds.Height, Is.GreaterThan(finalPortraitBounds.Width * 0.8), - "Should be back in portrait orientation"); - - // 15. Verify overall stability - border should be functional in final state - Assert.That(finalPortraitBounds.Width, Is.GreaterThan(0), - "Border should be visible in final portrait state"); - Assert.That(finalPortraitBounds.Height, Is.GreaterThan(0), - "Border should be visible in final portrait state"); - - // 16. Verify no errors occurred during complex interaction - var testStatus = App.WaitForElement("TestStatus"); - Assert.That(testStatus.GetText(), Does.Not.Contain("Error"), - "No errors should occur during orientation change with keyboard interaction"); - - // 17. Verify final state is stable (keyboard hidden) - Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should remain hidden in final state"); - } -#endif } #endif \ No newline at end of file diff --git a/src/Core/src/Platform/Android/SafeAreaExtensions.cs b/src/Core/src/Platform/Android/SafeAreaExtensions.cs index 5c029196975a..6bc35cb10cef 100644 --- a/src/Core/src/Platform/Android/SafeAreaExtensions.cs +++ b/src/Core/src/Platform/Android/SafeAreaExtensions.cs @@ -62,20 +62,6 @@ internal static SafeAreaRegions GetSafeAreaRegionForEdge(int edge, ICrossPlatfor var right = GetSafeAreaForEdge(GetSafeAreaRegionForEdge(2, layout), baseSafeArea.Right, 2, isKeyboardShowing, keyboardInsets); var bottom = GetSafeAreaForEdge(GetSafeAreaRegionForEdge(3, layout), baseSafeArea.Bottom, 3, isKeyboardShowing, keyboardInsets); - if (isKeyboardShowing && - context.GetActivity()?.Window is Window window && - window?.Attributes is WindowManagerLayoutParams attr) - { - // If the window is panned from the keyboard being open - // and there isn't a bottom inset to apply then just don't touch anything - var softInputMode = attr.SoftInputMode; - if (softInputMode == SoftInput.AdjustPan - && bottom == 0 - ) - { - return WindowInsetsCompat.Consumed; - } - } var globalWindowInsetsListener = context.GetGlobalWindowInsetListener(); bool hasTrackedViews = globalWindowInsetsListener?.HasTrackedView == true; @@ -237,7 +223,7 @@ internal static SafeAreaRegions GetSafeAreaRegionForEdge(int edge, ICrossPlatfor } else { - newWindowInsets = windowInsets; + newWindowInsets = windowInsets; } // Fallback: return the base safe area for legacy views @@ -248,27 +234,28 @@ internal static double GetSafeAreaForEdge(SafeAreaRegions safeAreaRegion, double { // Edge-to-edge content - no safe area padding if (safeAreaRegion == SafeAreaRegions.None) - { return 0; - } - // Handle SoftInput specifically - only apply keyboard insets for bottom edge when keyboard is showing - if (isKeyboardShowing && edge == 3) + // Handle SoftInput specifically for bottom edge when keyboard is showing + if (edge == 3 && SafeAreaEdges.IsSoftInput(safeAreaRegion)) { - if (SafeAreaEdges.IsSoftInput(safeAreaRegion)) - return keyBoardInsets.Bottom; - - // if they keyboard is showing then we will just return 0 for the bottom inset - // because that part of the view is covered by the keyboard so we don't want to pad the view - return 0; + return HandleSoftInputRegion(safeAreaRegion, originalSafeArea, isKeyboardShowing, keyBoardInsets); } - // All other regions respect safe area in some form - // This includes: - // - Default: Platform default behavior - // - All: Obey all safe area insets - // - Container: Content flows under keyboard but stays out of bars/notch - // - Any combination of the above flags + // All other regions respect safe area (Default, All, Container, etc.) return originalSafeArea; } + + static double HandleSoftInputRegion(SafeAreaRegions safeAreaRegion, double originalSafeArea, bool isKeyboardShowing, SafeAreaPadding keyBoardInsets) + { + if (!isKeyboardShowing) + { + // When keyboard is hidden, only apply safe area for "All" regions + return safeAreaRegion == SafeAreaRegions.All ? originalSafeArea : 0; + } + + // When keyboard is showing, use the larger of keyboard inset or original safe area + var keyboardInset = keyBoardInsets.Bottom; + return Math.Max(keyboardInset, originalSafeArea); + } }