From ccbb126ce1187528319085e7de211eca8ead4fd5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 4 Aug 2025 08:16:31 +0000 Subject: [PATCH] restrict souce of input event sto parent view on android (#19289) --- .../Avalonia.Android/AvaloniaActivity.cs | 5 +- .../Avalonia.Android/AvaloniaView.Input.cs | 67 +++++++++++++++++++ src/Android/Avalonia.Android/AvaloniaView.cs | 21 +----- .../Platform/Input/AndroidInputMethod.cs | 3 - .../Platform/SkiaPlatform/TopLevelImpl.cs | 30 ++------- 5 files changed, 79 insertions(+), 47 deletions(-) create mode 100644 src/Android/Avalonia.Android/AvaloniaView.Input.cs diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index fa3484f058..cf425d279e 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs @@ -8,10 +8,10 @@ using Android.OS; using Android.Runtime; using Android.Views; using AndroidX.AppCompat.App; -using Avalonia.Platform; using Avalonia.Android.Platform; using Avalonia.Android.Platform.Storage; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform; namespace Avalonia.Android; @@ -48,6 +48,9 @@ public class AvaloniaActivity : AppCompatActivity, IAvaloniaActivity SetContentView(_view); + // By default, the view isn't focused if the activity is created anew, so we force focus. + _view.RequestFocus(); + _listener = new GlobalLayoutListener(_view); _view.ViewTreeObserver?.AddOnGlobalLayoutListener(_listener); diff --git a/src/Android/Avalonia.Android/AvaloniaView.Input.cs b/src/Android/Avalonia.Android/AvaloniaView.Input.cs new file mode 100644 index 0000000000..c829be56be --- /dev/null +++ b/src/Android/Avalonia.Android/AvaloniaView.Input.cs @@ -0,0 +1,67 @@ +using System; +using Android.Views; +using Android.Views.InputMethods; +using Avalonia.Android.Platform.SkiaPlatform; + +namespace Avalonia.Android +{ + public partial class AvaloniaView : IInitEditorInfo + { + private Func? _initEditorInfo; + + public override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs) + { + return _initEditorInfo?.Invoke(_view, outAttrs!)!; + } + + void IInitEditorInfo.InitEditorInfo(Func init) + { + _initEditorInfo = init; + } + + protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, global::Android.Graphics.Rect? previouslyFocusedRect) + { + base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect); + _accessHelper.OnFocusChanged(gainFocus, (int)direction, previouslyFocusedRect); + } + + protected override bool DispatchHoverEvent(MotionEvent? e) + { + return _accessHelper.DispatchHoverEvent(e!) || base.DispatchHoverEvent(e); + } + + protected override bool DispatchGenericPointerEvent(MotionEvent? e) + { + var result = _view.PointerHelper.DispatchMotionEvent(e, out var callBase); + + var baseResult = callBase && base.DispatchGenericPointerEvent(e); + + return result ?? baseResult; + } + + public override bool DispatchTouchEvent(MotionEvent? e) + { + var result = _view.PointerHelper.DispatchMotionEvent(e, out var callBase); + var baseResult = callBase && base.DispatchTouchEvent(e); + + if(result == true) + { + // Request focus for this view + RequestFocus(); + } + + return result ?? baseResult; + } + + public override bool DispatchKeyEvent(KeyEvent? e) + { + var res = _view.KeyboardHelper.DispatchKeyEvent(e, out var callBase); + if (res == false) + callBase = !_accessHelper.DispatchKeyEvent(e!) && callBase; + + var baseResult = callBase && base.DispatchKeyEvent(e); + + return res ?? baseResult; + } + } +} diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index ced2f11077..665feb2e2b 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -17,7 +17,7 @@ using Avalonia.Rendering; namespace Avalonia.Android { - public class AvaloniaView : FrameLayout + public partial class AvaloniaView : FrameLayout { private EmbeddableControlRoot _root; private readonly ViewImpl _view; @@ -71,24 +71,6 @@ namespace Avalonia.Android _root = null!; } - protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, global::Android.Graphics.Rect? previouslyFocusedRect) - { - base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect); - _accessHelper.OnFocusChanged(gainFocus, (int)direction, previouslyFocusedRect); - } - - protected override bool DispatchHoverEvent(MotionEvent? e) - { - return _accessHelper.DispatchHoverEvent(e!) || base.DispatchHoverEvent(e); - } - - public override bool DispatchKeyEvent(KeyEvent? e) - { - if (!_view.View.DispatchKeyEvent(e)) - return _accessHelper.DispatchKeyEvent(e!) || base.DispatchKeyEvent(e); - return true; - } - [SupportedOSPlatform("android24.0")] public override void OnVisibilityAggregated(bool isVisible) { @@ -149,7 +131,6 @@ namespace Avalonia.Android { public ViewImpl(AvaloniaView avaloniaView) : base(avaloniaView) { - View.Focusable = true; View.FocusChange += ViewImpl_FocusChange; } diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs index 8003db6607..2e8e145ef8 100644 --- a/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/Platform/Input/AndroidInputMethod.cs @@ -44,9 +44,6 @@ namespace Avalonia.Android.Platform.Input public AndroidInputMethod(TView host) { - if (host.OnCheckIsTextEditor() == false) - throw new InvalidOperationException("Host should return true from OnCheckIsTextEditor()"); - _host = host; _imm = host.Context?.GetSystemService(Context.InputMethodService).JavaCast() ?? throw new InvalidOperationException("Context.InputMethodService is expected to be not null."); diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index bd90a91483..564f27cdfc 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -5,9 +5,7 @@ using Android.Content; using Android.Graphics; using Android.Graphics.Drawables; using Android.Runtime; -using Android.Text; using Android.Views; -using Android.Views.InputMethods; using AndroidX.AppCompat.App; using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Specific; @@ -37,7 +35,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly AndroidKeyboardEventsHelper _keyboardHelper; private readonly AndroidMotionEventsHelper _pointerHelper; - private readonly AndroidInputMethod _textInputMethod; + private readonly AndroidInputMethod _textInputMethod; private readonly INativeControlHostImpl _nativeControlHost; private readonly IStorageProvider? _storageProvider; private readonly AndroidSystemNavigationManagerImpl _systemNavigationManager; @@ -56,7 +54,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform } _view = new ViewImpl(avaloniaView.Context, this, placeOnTop); - _textInputMethod = new AndroidInputMethod(_view); + _textInputMethod = new AndroidInputMethod(avaloniaView); _keyboardHelper = new AndroidKeyboardEventsHelper(this); _pointerHelper = new AndroidMotionEventsHelper(this); _gl = new EglGlPlatformSurface(this); @@ -169,7 +167,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform Resized?.Invoke(size, WindowResizeReason.Layout); } - class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo + class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback { private readonly TopLevelImpl _tl; private Size _oldSize; @@ -246,24 +244,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform base.SurfaceChanged(holder, format, width, height); } - - public sealed override bool OnCheckIsTextEditor() - { - return true; - } - - private Func? _initEditorInfo; - - public void InitEditorInfo(Func init) - { - _initEditorInfo = init; - } - - public sealed override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs) - { - return _initEditorInfo?.Invoke(_tl, outAttrs!)!; - } - } public IPopupImpl? CreatePopup() => null; @@ -307,6 +287,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform public double Scaling => RenderScaling; + internal AndroidKeyboardEventsHelper KeyboardHelper => _keyboardHelper; + + internal AndroidMotionEventsHelper PointerHelper => _pointerHelper; + public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { if (_view.Context is not AvaloniaMainActivity activity)