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..0b34217faab8 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21109.cs @@ -0,0 +1,33 @@ +#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 EntryReturnTypeWorks() + { + App.WaitForElement("WaitForStubControl"); + + // 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); + } + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases/Issues/Issue21109.xaml b/src/Controls/tests/TestCases/Issues/Issue21109.xaml new file mode 100644 index 000000000000..4fb100db6541 --- /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..a4e871b0bf3b --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue21109.xaml.cs @@ -0,0 +1,65 @@ +using System; +using Microsoft.Maui; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Xaml; +using Microsoft.Maui.Hosting; + +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 Issue21109DecimalEntry : Entry + { + public Issue21109DecimalEntry() + { + Keyboard = Keyboard.Numeric; + } + } + + public static class Issue21109Extensions + { + public static MauiAppBuilder Issue21109AddMappers(this MauiAppBuilder builder) + { + builder.ConfigureMauiHandlers(handlers => + { + Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(nameof(Issue21109DecimalEntry), (handler, view) => + { + if (view is Issue21109DecimalEntry) + { +#if ANDROID + handler.PlatformView.KeyListener = new Platform.Issue21109NumericKeyListener(handler.PlatformView.InputType); +#endif + } + }); + }); + + return builder; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases/MauiProgram.cs b/src/Controls/tests/TestCases/MauiProgram.cs index 35d9a40575be..9f9ce1d3297a 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,22 @@ 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(); + .Issue21109AddMappers(); + + return appBuilder.Build(); + } } class App : Application diff --git a/src/Controls/tests/TestCases/Platforms/Android/Issue21109NumericKeyListener.cs b/src/Controls/tests/TestCases/Platforms/Android/Issue21109NumericKeyListener.cs new file mode 100644 index 000000000000..9ce1099c40bf --- /dev/null +++ b/src/Controls/tests/TestCases/Platforms/Android/Issue21109NumericKeyListener.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 Issue21109NumericKeyListener : NumberKeyListener +{ + public override InputTypes InputType { get; } + + protected override char[] GetAcceptedChars() => "0123456789-,.".ToCharArray(); + + public Issue21109NumericKeyListener(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 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