From 51ccfbe23ead0a69103cb0f5e1f99dee8dbdd621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 21 May 2024 11:49:44 +0200 Subject: [PATCH 1/5] Added repro sample --- .../tests/TestCases/Issues/Issue21109.xaml | 29 +++++++++++++ .../tests/TestCases/Issues/Issue21109.xaml.cs | 43 +++++++++++++++++++ src/Controls/tests/TestCases/MauiProgram.cs | 32 ++++++++++---- .../Platforms/Android/NumericKeyListener.cs | 25 +++++++++++ 4 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 src/Controls/tests/TestCases/Issues/Issue21109.xaml create mode 100644 src/Controls/tests/TestCases/Issues/Issue21109.xaml.cs create mode 100644 src/Controls/tests/TestCases/Platforms/Android/NumericKeyListener.cs diff --git a/src/Controls/tests/TestCases/Issues/Issue21109.xaml b/src/Controls/tests/TestCases/Issues/Issue21109.xaml new file mode 100644 index 000000000000..e2154e221bab --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue21109.xaml @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases/Issues/Issue21109.xaml.cs b/src/Controls/tests/TestCases/Issues/Issue21109.xaml.cs new file mode 100644 index 000000000000..5eb246833504 --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue21109.xaml.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.Maui; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Xaml; + +namespace Maui.Controls.Sample.Issues +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + [Issue(IssueTracker.Github, 21109, "[Android] MAUI 8.0.3 -> 8.0.6 regression: custom handler with key listener no longer works", PlatformAffected.All)] + public partial class Issue21109 : ContentPage + { + public Issue21109() + { + InitializeComponent(); + + ReturnTypeResult.Text = $"ReturnType: {ReturnTypeEntry.ReturnType}"; + } + + void OnReturnTypeEntryTextChanged(object sender, TextChangedEventArgs e) + { + Random rnd = new Random(); + var returnTypeCount = Enum.GetNames(typeof(ReturnType)).Length; + + ReturnType returnType = ReturnType.Default; + + do + { + returnType = (ReturnType)rnd.Next(0, returnTypeCount); + } while (returnType == ReturnTypeEntry.ReturnType); + + ReturnTypeEntry.ReturnType = returnType; + ReturnTypeResult.Text = $"ReturnType: {returnType}"; + } + } + + public class DecimalEntry : Entry + { + public DecimalEntry() + { + Keyboard = Keyboard.Numeric; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases/MauiProgram.cs b/src/Controls/tests/TestCases/MauiProgram.cs index 35d9a40575be..bfdb75bb77ca 100644 --- a/src/Controls/tests/TestCases/MauiProgram.cs +++ b/src/Controls/tests/TestCases/MauiProgram.cs @@ -1,4 +1,5 @@ using System; +using Maui.Controls.Sample.Issues; using Microsoft.Maui; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Hosting; @@ -8,18 +9,31 @@ namespace Maui.Controls.Sample { public static class MauiProgram { - public static MauiApp CreateMauiApp() => - MauiApp - .CreateBuilder() - #if IOS || ANDROID - .UseMauiMaps() - #endif - .UseMauiApp() + public static MauiApp CreateMauiApp() + { + var appBuilder = MauiApp.CreateBuilder(); + +#if IOS || ANDROID + appBuilder.UseMauiMaps(); +#endif + appBuilder.UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); - }) - .Build(); + }); + + Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(nameof(DecimalEntry), (handler, view) => + { + if (view is DecimalEntry) + { +#if ANDROID + handler.PlatformView.KeyListener = new Platform.NumericKeyListener(handler.PlatformView.InputType); +#endif + } + }); + + return appBuilder.Build(); + } } class App : Application diff --git a/src/Controls/tests/TestCases/Platforms/Android/NumericKeyListener.cs b/src/Controls/tests/TestCases/Platforms/Android/NumericKeyListener.cs new file mode 100644 index 000000000000..b315f1a90bcc --- /dev/null +++ b/src/Controls/tests/TestCases/Platforms/Android/NumericKeyListener.cs @@ -0,0 +1,25 @@ +using Android.Text; +using Android.Text.Method; +using Android.Views; +using Microsoft.Maui.Controls; +using AView = Android.Views.View; + +namespace Maui.Controls.Sample.Platform; + +public class NumericKeyListener : NumberKeyListener +{ + public override InputTypes InputType { get; } + + protected override char[] GetAcceptedChars() => "0123456789-,.".ToCharArray(); + + public NumericKeyListener(InputTypes inputType) + { + InputType = inputType; + } + + public override bool OnKeyDown(AView view, IEditable content, Keycode keyCode, KeyEvent e) + { + Application.Current.MainPage.DisplayAlert("OnKeyDown", string.Empty, "Ok"); + return base.OnKeyDown(view, content, keyCode, e); + } +} \ No newline at end of file From 5187ffbe3f3b6f22bb4e7746038fce69e9456f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 21 May 2024 11:49:55 +0200 Subject: [PATCH 2/5] Added UI Test --- .../Tests/Issues/Issue21109.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs new file mode 100644 index 000000000000..8f963d914c80 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs @@ -0,0 +1,36 @@ +#if ANDROID +using NUnit.Framework; +using NUnit.Framework.Legacy; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue21109 : _IssuesUITest + { + public Issue21109(TestDevice device) : base(device) { } + + public override string Issue => "[Android] MAUI 8.0.3 -> 8.0.6 regression: custom handler with key listener no longer works"; + + [Test] + [Category(UITestCategories.Entry)] + public void CustomEntryListenerWorks() + { + App.WaitForElement("WaitForStubControl"); + + // 1. Verify that ReturnType works as expected. + var returnType1 = App.FindElement("ReturnTypeResult").GetText(); + App.EnterText("ReturnTypeEntry", "a"); + var returnType2 = App.FindElement("ReturnTypeResult").GetText(); + ClassicAssert.AreNotEqual(returnType1, returnType2); + + // 2. Use a custom Entry with a NumberKeyListener. + App.Tap("CustomDecimalEntry"); + App.EnterText("CustomDecimalEntry", "1"); + + // 3. If appear an alert, the test passed. + VerifyScreenshot(); + } + } +} +#endif \ No newline at end of file From 838a9a713d935347d887185290a12e4b0cd8a004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 21 May 2024 11:50:21 +0200 Subject: [PATCH 3/5] Fix the issue --- src/Core/src/Handlers/Entry/EntryHandler.Android.cs | 10 ---------- src/Core/src/Platform/Android/EditTextExtensions.cs | 6 +++++- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs index 636f4d2532a4..587cee125e9d 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -42,7 +42,6 @@ public override void SetVirtualView(IView view) // TODO: NET8 issoto - Change the return type to MauiAppCompatEditText protected override void ConnectHandler(AppCompatEditText platformView) { - platformView.ViewAttachedToWindow += OnViewAttachedToWindow; platformView.TextChanged += OnTextChanged; platformView.FocusChange += OnFocusedChange; platformView.Touch += OnTouch; @@ -54,7 +53,6 @@ protected override void DisconnectHandler(AppCompatEditText platformView) { _clearButtonDrawable = null; - platformView.ViewAttachedToWindow -= OnViewAttachedToWindow; platformView.TextChanged -= OnTextChanged; platformView.FocusChange -= OnFocusedChange; platformView.Touch -= OnTouch; @@ -148,14 +146,6 @@ static void MapFocus(IEntryHandler handler, IEntry entry, object? args) handler.PlatformView.Focus(request); } - void OnViewAttachedToWindow(object? sender, ViewAttachedToWindowEventArgs e) - { - if (PlatformView is null || VirtualView is null) - return; - - PlatformView.UpdateReturnType(VirtualView); - } - void OnTextChanged(object? sender, TextChangedEventArgs e) { if (VirtualView == null) diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index c344b60d1081..ded06100cd6b 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using Android.Content; using Android.Content.Res; using Android.Graphics.Drawables; using Android.Text; @@ -203,6 +203,10 @@ public static void UpdateReturnType(this EditText editText, IEntry entry) { editText.SetInputType(entry); editText.ImeOptions = entry.ReturnType.ToPlatform(); + + // Restart the input on the current focused EditText + InputMethodManager? imm = (InputMethodManager?)editText.Context?.GetSystemService(Context.InputMethodService); + imm?.RestartInput(editText); } // TODO: NET8 issoto - Revisit this, marking this method as `internal` to avoid breaking public API changes From 4575fa83d717c267cdc89c4e00ed9a4346bf7f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 21 May 2024 13:38:10 +0200 Subject: [PATCH 4/5] Updated test --- .../Tests/Issues/Issue21109.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs index 8f963d914c80..0b34217faab8 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs @@ -14,22 +14,19 @@ public Issue21109(TestDevice device) : base(device) { } [Test] [Category(UITestCategories.Entry)] - public void CustomEntryListenerWorks() + public void EntryReturnTypeWorks() { App.WaitForElement("WaitForStubControl"); - // 1. Verify that ReturnType works as expected. + // Verify that ReturnType works as expected. + if (App.IsKeyboardShown()) + App.DismissKeyboard(); + var returnType1 = App.FindElement("ReturnTypeResult").GetText(); + App.Tap("ReturnTypeEntry"); App.EnterText("ReturnTypeEntry", "a"); var returnType2 = App.FindElement("ReturnTypeResult").GetText(); ClassicAssert.AreNotEqual(returnType1, returnType2); - - // 2. Use a custom Entry with a NumberKeyListener. - App.Tap("CustomDecimalEntry"); - App.EnterText("CustomDecimalEntry", "1"); - - // 3. If appear an alert, the test passed. - VerifyScreenshot(); } } } From 9174df5d1f24f88e46d90c748d7d7a7a3129ccbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Wed, 22 May 2024 09:59:08 +0200 Subject: [PATCH 5/5] Renamed Listener --- .../tests/TestCases/Issues/Issue21109.xaml | 2 +- .../tests/TestCases/Issues/Issue21109.xaml.cs | 26 +++++++++++++++++-- src/Controls/tests/TestCases/MauiProgram.cs | 13 ++-------- ...ner.cs => Issue21109NumericKeyListener.cs} | 4 +-- 4 files changed, 29 insertions(+), 16 deletions(-) rename src/Controls/tests/TestCases/Platforms/Android/{NumericKeyListener.cs => Issue21109NumericKeyListener.cs} (83%) diff --git a/src/Controls/tests/TestCases/Issues/Issue21109.xaml b/src/Controls/tests/TestCases/Issues/Issue21109.xaml index e2154e221bab..4fb100db6541 100644 --- a/src/Controls/tests/TestCases/Issues/Issue21109.xaml +++ b/src/Controls/tests/TestCases/Issues/Issue21109.xaml @@ -23,7 +23,7 @@ AutomationId="ReturnTypeResult"/>