-
Notifications
You must be signed in to change notification settings - Fork 2k
[Android] Fix bottom safe area padding dropping to zero when keyboard is shown #35084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
83e4b39
2936b3d
00093be
af01b74
d886e09
b71adea
f8cb875
62d020c
4f61d65
cb0e786
304df68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| #if ANDROID | ||
| using Android.Views; | ||
| using AView = Android.Views.View; | ||
| #endif | ||
|
|
||
| namespace Maui.Controls.Sample.Issues; | ||
|
|
||
| [Issue(IssueTracker.Github, 32871, "[Android] Bottom insets issues when keyboard is shown", PlatformAffected.Android)] | ||
| public partial class Issue32871 : ContentPage | ||
| { | ||
| public Issue32871() | ||
| { | ||
| SafeAreaEdges = SafeAreaEdges.None; | ||
| BackgroundColor = Colors.Green; | ||
|
|
||
| var paddingLabel = new Label | ||
| { | ||
| Text = "waiting", | ||
| AutomationId = "PaddingLabel", | ||
| TextColor = Colors.White, | ||
| FontSize = 12 | ||
| }; | ||
|
|
||
| var entry = new Entry | ||
| { | ||
| Placeholder = "Tap here to show keyboard", | ||
| AutomationId = "TestEntry", | ||
| VerticalOptions = LayoutOptions.Start, | ||
| HorizontalOptions = LayoutOptions.Fill, | ||
| HeightRequest = 56 | ||
| }; | ||
|
|
||
| var grid = new Grid | ||
| { | ||
| AutomationId = "MainGrid", | ||
| SafeAreaEdges = SafeAreaEdges.Default, | ||
| BackgroundColor = Colors.Red, | ||
| RowDefinitions = | ||
| { | ||
| new RowDefinition(80), | ||
| new RowDefinition(GridLength.Auto), | ||
| new RowDefinition(GridLength.Star), | ||
| new RowDefinition(GridLength.Auto) | ||
| } | ||
| }; | ||
|
|
||
| var label = new Label | ||
| { | ||
| Text = "Issue 32871", | ||
| AutomationId = "HeaderLabel", | ||
| HorizontalTextAlignment = Microsoft.Maui.TextAlignment.Center, | ||
| VerticalOptions = LayoutOptions.Start, | ||
| TextColor = Colors.White | ||
| }; | ||
|
|
||
| var bottomButton = new Button | ||
| { | ||
| Text = "Bottom Button", | ||
| AutomationId = "BottomButton", | ||
| BackgroundColor = Colors.Blue, | ||
| TextColor = Colors.White | ||
| }; | ||
|
|
||
| Grid.SetRow(label, 0); | ||
| Grid.SetRow(paddingLabel, 1); | ||
| Grid.SetRow(entry, 2); | ||
| Grid.SetRow(bottomButton, 3); | ||
|
|
||
| grid.Children.Add(label); | ||
| grid.Children.Add(paddingLabel); | ||
| grid.Children.Add(entry); | ||
| grid.Children.Add(bottomButton); | ||
|
|
||
| Content = grid; | ||
|
|
||
| SetupPlatform(grid, paddingLabel); | ||
| } | ||
|
|
||
| partial void SetupPlatform(Grid grid, Label paddingLabel); | ||
|
|
||
| protected override void OnDisappearing() | ||
| { | ||
| base.OnDisappearing(); | ||
| CleanupPlatform(); | ||
| } | ||
|
|
||
| partial void CleanupPlatform(); | ||
| } | ||
|
|
||
| #if ANDROID | ||
| public partial class Issue32871 | ||
| { | ||
| SoftInput _previousSoftInputMode; | ||
|
|
||
| partial void SetupPlatform(Grid grid, Label paddingLabel) | ||
| { | ||
| var window = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity?.Window; | ||
| if (window?.Attributes is WindowManagerLayoutParams attr) | ||
| { | ||
| _previousSoftInputMode = attr.SoftInputMode; | ||
| } | ||
| window?.SetSoftInputMode(SoftInput.AdjustUnspecified | SoftInput.StateHidden); | ||
|
|
||
| grid.HandlerChanged += (s, e) => | ||
| { | ||
| if (grid.Handler?.PlatformView is AView nativeView) | ||
| { | ||
| paddingLabel.Text = $"NativePadding: B={nativeView.PaddingBottom}"; | ||
| nativeView.AddOnLayoutChangeListener(new LayoutListener(nativeView, paddingLabel)); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| partial void CleanupPlatform() | ||
| { | ||
| var window = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity?.Window; | ||
| window?.SetSoftInputMode(_previousSoftInputMode); | ||
| } | ||
|
|
||
| class LayoutListener : Java.Lang.Object, AView.IOnLayoutChangeListener | ||
| { | ||
| readonly WeakReference<AView> _view; | ||
| readonly WeakReference<Label> _label; | ||
|
|
||
| public LayoutListener(AView view, Label label) | ||
| { | ||
| _view = new WeakReference<AView>(view); | ||
| _label = new WeakReference<Label>(label); | ||
| } | ||
|
|
||
| public void OnLayoutChange(AView v, int left, int top, int right, int bottom, | ||
| int oldLeft, int oldTop, int oldRight, int oldBottom) | ||
| { | ||
| if (_view.TryGetTarget(out var view) && _label.TryGetTarget(out var label)) | ||
| { | ||
| label.Text = $"NativePadding: B={view.PaddingBottom}"; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #if ANDROID // Android-only: fix and native padding assertion rely on Android platform code | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| using NUnit.Framework; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| using UITest.Appium; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| using UITest.Core; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Microsoft.Maui.TestCases.Tests.Issues; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class Issue32871 : _IssuesUITest | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| public override string Issue => "[Android] Bottom insets issues when keyboard is shown"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Issue32871(TestDevice device) : base(device) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Category(UITestCategories.SafeAreaEdges)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void BottomPaddingShouldBePreservedWhileKeyboardIsShowing() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| App.WaitForElement("MainGrid"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| App.WaitForTextToBePresentInElement("PaddingLabel", "NativePadding"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| var initialPaddingText = App.FindElement("PaddingLabel").GetText() ?? ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| var initialBottomPadding = ExtractBottomPadding(initialPaddingText); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (initialBottomPadding <= 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.Ignore("Device has no navigation bar bottom inset — cannot validate this regression."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+30
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| App.Tap("TestEntry"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.That(App.WaitForKeyboardToShow(), Is.True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Keyboard must be visible to validate the fix."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| var paddingWhileKeyboard = App.FindElement("PaddingLabel").GetText() ?? ""; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| var bottomPaddingDuringKeyboard = ExtractBottomPadding(paddingWhileKeyboard); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.That(bottomPaddingDuringKeyboard, Is.EqualTo(initialBottomPadding), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| $"Bottom padding should be preserved while keyboard is showing. " + | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| $"Initial: {initialBottomPadding}px, During keyboard: {bottomPaddingDuringKeyboard}px."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+42
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.That(App.WaitForKeyboardToShow(), Is.True, | |
| "Keyboard must be visible to validate the fix."); | |
| var paddingWhileKeyboard = App.FindElement("PaddingLabel").GetText() ?? ""; | |
| var bottomPaddingDuringKeyboard = ExtractBottomPadding(paddingWhileKeyboard); | |
| Assert.That(bottomPaddingDuringKeyboard, Is.EqualTo(initialBottomPadding), | |
| $"Bottom padding should be preserved while keyboard is showing. " + | |
| $"Initial: {initialBottomPadding}px, During keyboard: {bottomPaddingDuringKeyboard}px."); | |
| try | |
| { | |
| Assert.That(App.WaitForKeyboardToShow(), Is.True, | |
| "Keyboard must be visible to validate the fix."); | |
| var paddingWhileKeyboard = App.FindElement("PaddingLabel").GetText() ?? ""; | |
| var bottomPaddingDuringKeyboard = ExtractBottomPadding(paddingWhileKeyboard); | |
| Assert.That(bottomPaddingDuringKeyboard, Is.EqualTo(initialBottomPadding), | |
| $"Bottom padding should be preserved while keyboard is showing. " + | |
| $"Initial: {initialBottomPadding}px, During keyboard: {bottomPaddingDuringKeyboard}px."); | |
| } | |
| finally | |
| { | |
| App.DismissKeyboard(); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grid.HandlerChangedsubscribes with a lambda andAddOnLayoutChangeListener(...)creates a listener instance that is never removed or disposed. If the handler changes (or the page is reopened), this can register multiple listeners and leak Java-side objects. Store the listener (and the handler-changed delegate) as fields, unsubscribe inCleanupPlatform, and callRemoveOnLayoutChangeListener+Dispose()on the listener.