diff --git a/src/Controls/src/Core/Editor/Editor.Mapper.cs b/src/Controls/src/Core/Editor/Editor.Mapper.cs index 01ce7ee9e79e..d2dc2b69a706 100644 --- a/src/Controls/src/Core/Editor/Editor.Mapper.cs +++ b/src/Controls/src/Core/Editor/Editor.Mapper.cs @@ -17,6 +17,7 @@ public partial class Editor #if IOS || ANDROID EditorHandler.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused); + EditorHandler.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible); #endif #if ANDROID diff --git a/src/Controls/src/Core/Entry/Entry.Mapper.cs b/src/Controls/src/Core/Entry/Entry.Mapper.cs index 413f01b94078..9acb2b4127b6 100644 --- a/src/Controls/src/Core/Entry/Entry.Mapper.cs +++ b/src/Controls/src/Core/Entry/Entry.Mapper.cs @@ -22,6 +22,7 @@ public partial class Entry #if IOS || ANDROID EntryHandler.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused); + EntryHandler.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible); #endif #if ANDROID diff --git a/src/Controls/src/Core/InputView/InputView.Platform.cs b/src/Controls/src/Core/InputView/InputView.Platform.cs index 31717388ee4c..d71f0c7506f4 100644 --- a/src/Controls/src/Core/InputView/InputView.Platform.cs +++ b/src/Controls/src/Core/InputView/InputView.Platform.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; namespace Microsoft.Maui.Controls { @@ -14,6 +15,21 @@ internal static void MapIsFocused(IViewHandler handler, IView view) ?.UpdateFocusForView(iv); } } + + internal static void MapIsVisible(IViewHandler handler, IView view) + { + if (view is not InputView inputView || handler?.PlatformView == null) + { + return; + } + + // Prevent input queuing when InputView is hidden + // Dismiss soft keyboard on Android/iOS to stop background input processing + if (!inputView.IsVisible && inputView.IsSoftInputShowing()) + { + inputView.HideSoftInputAsync(CancellationToken.None); + } + } #endif } } diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEditorsNotVisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEditorsNotVisible.png new file mode 100644 index 000000000000..b4277a769623 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEditorsNotVisible.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27236.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27236.cs new file mode 100644 index 000000000000..e4872a631bf1 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27236.cs @@ -0,0 +1,53 @@ +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 27236, "android allows type into hidden Entry control", PlatformAffected.Android | PlatformAffected.iOS)] + public partial class Issue27236 : TestContentPage + { + protected override void Init() + { + StackLayout mainLayout = new StackLayout + { + Padding = new Thickness(10) + }; + + Entry toggleableEntry = new Entry + { + AutomationId = "ToggleableEntry", + IsVisible = false + }; + + Editor toggleableEditor = new Editor + { + AutomationId = "ToggleableEditor", + IsVisible = false + }; + + Button toggleEntryVisibilityButton = new Button + { + AutomationId = "ToggleEntryVisibilityButton", + Text = "Toggle Entry Visibility" + }; + + Button toggleEditorVisibilityButton = new Button + { + AutomationId = "ToggleEditorVisibilityButton", + Text = "Toggle Editor Visibility" + }; + + toggleEntryVisibilityButton.Clicked += (sender, e) => ToggleVisibility(toggleableEntry); + toggleEditorVisibilityButton.Clicked += (sender, e) => ToggleVisibility(toggleableEditor); + + void ToggleVisibility(VisualElement element) + { + element.IsVisible = !element.IsVisible; + } + + mainLayout.Children.Add(toggleableEntry); + mainLayout.Children.Add(toggleableEditor); + mainLayout.Children.Add(toggleEntryVisibilityButton); + mainLayout.Children.Add(toggleEditorVisibilityButton); + + Content = mainLayout; + } + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27236.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27236.cs new file mode 100644 index 000000000000..1b56200493e8 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27236.cs @@ -0,0 +1,45 @@ +#if ANDROID || IOS // Desktop platforms do not have a soft keyboard, so the test is restricted to Android and iOS. +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue27236 : _IssuesUITest + { + public Issue27236(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "android allows type into hidden Entry control"; + + [Test, Order(1)] + [Category(UITestCategories.Entry)] + public void VerifyEntryKeyboardVisibilityToggle() + { + App.WaitForElement("ToggleEntryVisibilityButton"); + App.Tap("ToggleEntryVisibilityButton"); + var element = App.WaitForElement("ToggleableEntry"); + element.SendKeys("Hello, Entry"); + App.Tap("ToggleEntryVisibilityButton"); + var keyboardVisible = App.IsKeyboardShown(); + Assert.That(keyboardVisible, Is.False); + VerifyScreenshot("VerifyEditorsNotVisible"); + } + + [Test, Order(2)] + [Category(UITestCategories.Editor)] + public void VerifyEditorKeyboardVisibilityToggle() + { + App.WaitForElement("ToggleEditorVisibilityButton"); + App.Tap("ToggleEditorVisibilityButton"); + var element = App.WaitForElement("ToggleableEditor"); + element.SendKeys("Hello, Editor"); + App.Tap("ToggleEditorVisibilityButton"); + var keyboardVisible = App.IsKeyboardShown(); + Assert.That(keyboardVisible, Is.False); + VerifyScreenshot("VerifyEditorsNotVisible"); + } + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorsNotVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorsNotVisible.png new file mode 100644 index 000000000000..7748ac7f01d6 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorsNotVisible.png differ