Browse Source

restrict souce of input event sto parent view on android (#19289)

Emmanuel Hansen 4 months ago
parent
commit
74e8ce7efa

+ 4 - 1
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);

+ 67 - 0
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<TopLevelImpl, EditorInfo, IInputConnection>? _initEditorInfo;
+
+        public override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs)
+        {
+            return _initEditorInfo?.Invoke(_view, outAttrs!)!;
+        }
+
+        void IInitEditorInfo.InitEditorInfo(Func<TopLevelImpl, EditorInfo, IInputConnection> 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;
+        }
+    }
+}

+ 1 - 20
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;
             }
 

+ 0 - 3
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<InputMethodManager>()
                    ?? throw new InvalidOperationException("Context.InputMethodService is expected to be not null.");

+ 10 - 51
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;
@@ -15,13 +13,11 @@ using Avalonia.Android.Platform.Specific.Helpers;
 using Avalonia.Android.Platform.Storage;
 using Avalonia.Controls;
 using Avalonia.Controls.Platform;
-using Avalonia.Controls.Platform.Surfaces;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Raw;
 using Avalonia.Input.TextInput;
 using Avalonia.OpenGL.Egl;
-using Avalonia.OpenGL.Surfaces;
 using Avalonia.Platform;
 using Avalonia.Platform.Storage;
 using Avalonia.Rendering.Composition;
@@ -34,7 +30,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
     {
         private readonly AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
         private readonly AndroidMotionEventsHelper _pointerHelper;
-        private readonly AndroidInputMethod<ViewImpl> _textInputMethod;
+        private readonly AndroidInputMethod<AvaloniaView> _textInputMethod;
         private readonly INativeControlHostImpl _nativeControlHost;
         private readonly IStorageProvider? _storageProvider;
         private readonly AndroidSystemNavigationManagerImpl _systemNavigationManager;
@@ -42,7 +38,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         private readonly ClipboardImpl _clipboard;
         private readonly AndroidLauncher? _launcher;
         private readonly AndroidScreens? _screens;
-        private ViewImpl _view;
+        private SurfaceViewImpl _view;
         private WindowTransparencyLevel _transparencyLevel;
 
         public TopLevelImpl(AvaloniaView avaloniaView, bool placeOnTop = false)
@@ -52,8 +48,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                 throw new ArgumentException("AvaloniaView.Context must not be null");
             }
 
-            _view = new ViewImpl(avaloniaView.Context, this, placeOnTop);
-            _textInputMethod = new AndroidInputMethod<ViewImpl>(_view);
+            _view = new SurfaceViewImpl(avaloniaView.Context, this, placeOnTop);
+            _textInputMethod = new AndroidInputMethod<AvaloniaView>(avaloniaView);
             _keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this);
             _pointerHelper = new AndroidMotionEventsHelper(this);
             _clipboard = new ClipboardImpl(avaloniaView.Context.GetSystemService(Context.ClipboardService).JavaCast<ClipboardManager>());
@@ -141,13 +137,13 @@ namespace Avalonia.Android.Platform.SkiaPlatform
             Resized?.Invoke(size, WindowResizeReason.Layout);
         }
 
-        sealed class ViewImpl : InvalidationAwareSurfaceView, IInitEditorInfo
+        sealed class SurfaceViewImpl : InvalidationAwareSurfaceView
         {
             private readonly TopLevelImpl _tl;
             private Size _oldSize;
             private double _oldScaling;
 
-            public ViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context)
+            public SurfaceViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context)
             {
                 _tl = tl;
                 if (placeOnTop)
@@ -176,30 +172,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                 base.DispatchDraw(canvas);
             }
 
-            protected override bool DispatchGenericPointerEvent(MotionEvent? e)
-            {
-                var result = _tl._pointerHelper.DispatchMotionEvent(e, out var callBase);
-                var baseResult = callBase && base.DispatchGenericPointerEvent(e);
-
-                return result ?? baseResult;
-            }
-
-            public override bool DispatchTouchEvent(MotionEvent? e)
-            {
-                var result = _tl._pointerHelper.DispatchMotionEvent(e, out var callBase);
-                var baseResult = callBase && base.DispatchTouchEvent(e);
-
-                return result ?? baseResult;
-            }
-
-            public override bool DispatchKeyEvent(KeyEvent? e)
-            {
-                var res = _tl._keyboardHelper.DispatchKeyEvent(e, out var callBase);
-                var baseResult = callBase && base.DispatchKeyEvent(e);
-
-                return res ?? baseResult;
-            }
-
             public override void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
             {
                 base.SurfaceChanged(holder, format, width, height);
@@ -232,23 +204,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                 _tl.Compositor.RequestCompositionUpdate(drawingFinished.Run);
                 base.SurfaceRedrawNeededAsync(holder, drawingFinished);
             }
-
-            public override bool OnCheckIsTextEditor()
-            {
-                return true;
-            }
-
-            private Func<TopLevelImpl, EditorInfo, IInputConnection>? _initEditorInfo;
-
-            public void InitEditorInfo(Func<TopLevelImpl, EditorInfo, IInputConnection> init)
-            {
-                _initEditorInfo = init;
-            }
-
-            public override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs)
-            {
-                return _initEditorInfo?.Invoke(_tl, outAttrs!)!;
-            }
         }
 
         public IPopupImpl? CreatePopup() => null;
@@ -291,6 +246,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size => _view.Size;
         double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => _view.Scaling;
 
+        internal AndroidKeyboardEventsHelper<TopLevelImpl> KeyboardHelper => _keyboardHelper;
+
+        internal AndroidMotionEventsHelper PointerHelper => _pointerHelper;
+
         public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels)
         {
             if (_view.Context is not AvaloniaMainActivity activity)