From c530956a0b003cf0bafe89419c83cc5e94a933cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Fri, 19 Mar 2021 22:17:27 +0000 Subject: [PATCH 1/4] Avalonia.Android code cleanup. --- .../Avalonia.Android/AndroidPlatform.cs | 22 +------------------ .../AndroidThreadingInterface.cs | 15 +++++++------ .../InvalidationAwareSurfaceView.cs | 2 ++ src/Android/Avalonia.Android/app.config | 11 ---------- 4 files changed, 11 insertions(+), 39 deletions(-) delete mode 100644 src/Android/Avalonia.Android/app.config diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index e0ed9b1fda..5e11d8eab2 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -29,21 +29,12 @@ namespace Avalonia namespace Avalonia.Android { - class AndroidPlatform : IPlatformSettings, IWindowingPlatform + class AndroidPlatform : IPlatformSettings { public static readonly AndroidPlatform Instance = new AndroidPlatform(); public static AndroidPlatformOptions Options { get; private set; } public Size DoubleClickSize => new Size(4, 4); public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200); - public double RenderScalingFactor => _scalingFactor; - public double LayoutScalingFactor => _scalingFactor; - - private readonly double _scalingFactor = 1; - - public AndroidPlatform() - { - _scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity; - } public static void Initialize(Type appType, AndroidPlatformOptions options) { @@ -56,7 +47,6 @@ namespace Avalonia.Android .Bind().ToConstant(Instance) .Bind().ToConstant(new AndroidThreadingInterface()) .Bind().ToTransient() - .Bind().ToConstant(Instance) .Bind().ToSingleton() .Bind().ToConstant(new ChoreographerTimer()) .Bind().ToConstant(new RenderLoop()) @@ -70,16 +60,6 @@ namespace Avalonia.Android EglPlatformOpenGlInterface.TryInitialize(); } } - - public IWindowImpl CreateWindow() - { - throw new NotSupportedException(); - } - - public IWindowImpl CreateEmbeddableWindow() - { - throw new NotSupportedException(); - } } public sealed class AndroidPlatformOptions diff --git a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs index 6fe77adca1..e72f0aed90 100644 --- a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs +++ b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs @@ -1,25 +1,26 @@ using System; using System.Reactive.Disposables; using System.Threading; + using Android.OS; + using Avalonia.Platform; using Avalonia.Threading; +using App = Android.App.Application; + namespace Avalonia.Android { - class AndroidThreadingInterface : IPlatformThreadingInterface + internal sealed class AndroidThreadingInterface : IPlatformThreadingInterface { private Handler _handler; public AndroidThreadingInterface() { - _handler = new Handler(global::Android.App.Application.Context.MainLooper); + _handler = new Handler(App.Context.MainLooper); } - public void RunLoop(CancellationToken cancellationToken) - { - return; - } + public void RunLoop(CancellationToken cancellationToken) => throw new NotSupportedException(); public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { @@ -57,7 +58,7 @@ namespace Avalonia.Android }); } }, null, TimeSpan.Zero, interval); - + return Disposable.Create(() => { lock (l) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs index 02ea702236..16c5bdae3d 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs @@ -43,11 +43,13 @@ namespace Avalonia.Android } } + [Obsolete("deprecated")] public override void Invalidate(global::Android.Graphics.Rect dirty) { Invalidate(); } + [Obsolete("deprecated")] public override void Invalidate(int l, int t, int r, int b) { Invalidate(); diff --git a/src/Android/Avalonia.Android/app.config b/src/Android/Avalonia.Android/app.config deleted file mode 100644 index fc064cedfb..0000000000 --- a/src/Android/Avalonia.Android/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file From 7fb6e14716712ff2168708b909b017bd8c7b0096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sat, 20 Mar 2021 16:26:19 +0000 Subject: [PATCH 2/4] Use TouchDevice on Android. --- .../Avalonia.Android/AvaloniaActivity.cs | 8 +- .../Platform/Input/AndroidMouseDevice.cs | 14 --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 10 +- .../Helpers/AndroidTouchEventsHelper.cs | 91 +++++-------------- 4 files changed, 28 insertions(+), 95 deletions(-) delete mode 100644 src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index 52b68f8e2f..b3a7585520 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs @@ -1,4 +1,3 @@ - using Android.App; using Android.OS; using Android.Views; @@ -7,7 +6,6 @@ namespace Avalonia.Android { public abstract class AvaloniaActivity : Activity { - internal AvaloniaView View; object _content; @@ -35,9 +33,7 @@ namespace Avalonia.Android } } - public override bool DispatchKeyEvent(KeyEvent e) - { - return View.DispatchKeyEvent(e); - } + public override bool DispatchKeyEvent(KeyEvent e) => + View.DispatchKeyEvent(e) ? true : base.DispatchKeyEvent(e); } } diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs deleted file mode 100644 index d52eeb15e4..0000000000 --- a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Avalonia.Input; - -namespace Avalonia.Android.Platform.Input -{ - public class AndroidMouseDevice : MouseDevice - { - public static AndroidMouseDevice Instance { get; } = new AndroidMouseDevice(); - - public AndroidMouseDevice() - { - - } - } -} \ No newline at end of file diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 10bf414f25..4fd9bc040b 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -6,7 +6,6 @@ using Android.Runtime; using Android.Views; using Avalonia.Android.OpenGL; -using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Controls; @@ -35,7 +34,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _view = new ViewImpl(context, this, placeOnTop); _keyboardHelper = new AndroidKeyboardEventsHelper(this); _touchHelper = new AndroidTouchEventsHelper(this, () => InputRoot, - p => GetAvaloniaPointFromEvent(p)); + GetAvaloniaPointFromEvent); _gl = GlPlatformSurface.TryCreate(this); _framebuffer = new FramebufferManager(this); @@ -44,8 +43,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform _view.Resources.DisplayMetrics.HeightPixels); } - - private bool _handleEvents; public bool HandleEvents @@ -58,7 +55,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform } } - public virtual Point GetAvaloniaPointFromEvent(MotionEvent e) => new Point(e.GetX(), e.GetY()); + public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) => + new Point(e.GetX(pointerIndex), e.GetY(pointerIndex)) / RenderScaling; public IInputRoot InputRoot { get; private set; } @@ -76,7 +74,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform } } - public IMouseDevice MouseDevice => AndroidMouseDevice.Instance; + public IMouseDevice MouseDevice { get; } = new MouseDevice(); public Action Closed { get; set; } diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs index 0bfbb1c2df..6142598514 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs @@ -11,7 +11,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers private TView _view; public bool HandleEvents { get; set; } - public AndroidTouchEventsHelper(TView view, Func getInputRoot, Func getPointfunc) + public AndroidTouchEventsHelper(TView view, Func getInputRoot, Func getPointfunc) { this._view = view; HandleEvents = true; @@ -19,11 +19,9 @@ namespace Avalonia.Android.Platform.Specific.Helpers _getInputRoot = getInputRoot; } - private DateTime _lastTouchMoveEventTime = DateTime.Now; - private Point? _lastTouchMovePoint; - private Func _getPointFunc; + private TouchDevice _touchDevice = new TouchDevice(); + private Func _getPointFunc; private Func _getInputRoot; - private Point _point; public bool? DispatchTouchEvent(MotionEvent e, out bool callBase) { @@ -33,89 +31,44 @@ namespace Avalonia.Android.Platform.Specific.Helpers return null; } - RawPointerEventType? mouseEventType = null; var eventTime = DateTime.Now; + //Basic touch support - switch (e.Action) + var pointerEventType = e.Action switch { - case MotionEventActions.Move: - //may be bot flood the evnt system with too many event especially on not so powerfull mobile devices - if ((eventTime - _lastTouchMoveEventTime).TotalMilliseconds > 10) - { - mouseEventType = RawPointerEventType.Move; - } - break; - - case MotionEventActions.Down: - mouseEventType = RawPointerEventType.LeftButtonDown; + MotionEventActions.Down => RawPointerEventType.TouchBegin, + MotionEventActions.Up => RawPointerEventType.TouchEnd, + MotionEventActions.Cancel => RawPointerEventType.TouchCancel, + _ => RawPointerEventType.TouchUpdate + }; - break; + if (e.Action.HasFlag(MotionEventActions.PointerDown)) + { + pointerEventType = RawPointerEventType.TouchBegin; + } - case MotionEventActions.Up: - mouseEventType = RawPointerEventType.LeftButtonUp; - break; + if (e.Action.HasFlag(MotionEventActions.PointerUp)) + { + pointerEventType = RawPointerEventType.TouchEnd; } - if (mouseEventType != null) + for (int i = 0; i < e.PointerCount; i++) { //if point is in view otherwise it's possible avalonia not to find the proper window to dispatch the event - _point = _getPointFunc(e); + var point = _getPointFunc(e, i); double x = _view.View.GetX(); double y = _view.View.GetY(); double r = x + _view.View.Width; double b = y + _view.View.Height; - if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y) + if (x <= point.X && r >= point.X && y <= point.Y && b >= point.Y) { var inputRoot = _getInputRoot(); - var mouseDevice = Avalonia.Android.Platform.Input.AndroidMouseDevice.Instance; - - //in order the controls to work in a predictable way - //we need to generate mouse move before first mouse down event - //as this is the way buttons are working every time - //otherwise there is a problem sometimes - if (mouseEventType == RawPointerEventType.LeftButtonDown) - { - var me = new RawPointerEventArgs(mouseDevice, (uint)eventTime.Ticks, inputRoot, - RawPointerEventType.Move, _point, RawInputModifiers.None); - _view.Input(me); - } - var mouseEvent = new RawPointerEventArgs(mouseDevice, (uint)eventTime.Ticks, inputRoot, - mouseEventType.Value, _point, RawInputModifiers.LeftMouseButton); + var mouseEvent = new RawTouchEventArgs(_touchDevice, (uint)eventTime.Ticks, inputRoot, + i == e.ActionIndex ? pointerEventType : RawPointerEventType.TouchUpdate, point, RawInputModifiers.None, e.GetPointerId(i)); _view.Input(mouseEvent); - - if (e.Action == MotionEventActions.Move && mouseDevice.Captured == null) - { - if (_lastTouchMovePoint != null) - { - //raise mouse scroll event so the scrollers - //are moving with the cursor - double vectorX = _point.X - _lastTouchMovePoint.Value.X; - double vectorY = _point.Y - _lastTouchMovePoint.Value.Y; - //based on test correction of 0.02 is working perfect - double correction = 0.02; - var ps = AndroidPlatform.Instance.LayoutScalingFactor; - var mouseWheelEvent = new RawMouseWheelEventArgs( - mouseDevice, - (uint)eventTime.Ticks, - inputRoot, - _point, - new Vector(vectorX * correction / ps, vectorY * correction / ps), RawInputModifiers.LeftMouseButton); - _view.Input(mouseWheelEvent); - } - _lastTouchMovePoint = _point; - _lastTouchMoveEventTime = eventTime; - } - else if (e.Action == MotionEventActions.Down) - { - _lastTouchMovePoint = _point; - } - else - { - _lastTouchMovePoint = null; - } } } From 0257bdf6eddfa63b770c644aab41dc7ed18670c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sun, 21 Mar 2021 01:37:35 +0000 Subject: [PATCH 3/4] Implemented render scaling on Android. --- .../SkiaPlatform/AndroidFramebuffer.cs | 6 ++-- .../SkiaPlatform/FramebufferManager.cs | 2 +- .../Platform/SkiaPlatform/TopLevelImpl.cs | 28 ++++++------------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs index 2afa4e83f1..b115917622 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs @@ -10,7 +10,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform { private IntPtr _window; - public AndroidFramebuffer(Surface surface) + public AndroidFramebuffer(Surface surface, double scaling) { if(surface == null) throw new ArgumentNullException(nameof(surface)); @@ -31,6 +31,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4); Address = buffer.bits; + + Dpi = scaling * new Vector(96, 96); } public void Dispose() @@ -44,7 +46,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IntPtr Address { get; set; } public PixelSize Size { get; } public int RowBytes { get; } - public Vector Dpi { get; } = new Vector(96, 96); + public Vector Dpi { get; } public PixelFormat Format { get; } [DllImport("android")] diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs index 18c4796fae..56a4eb22d4 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs @@ -12,6 +12,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform _topLevel = topLevel; } - public ILockedFramebuffer Lock() => new AndroidFramebuffer(_topLevel.InternalView.Holder.Surface); + public ILockedFramebuffer Lock() => new AndroidFramebuffer(_topLevel.InternalView.Holder.Surface, _topLevel.RenderScaling); } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 4fd9bc040b..fe237a1719 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -39,8 +39,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform _gl = GlPlatformSurface.TryCreate(this); _framebuffer = new FramebufferManager(this); - MaxClientSize = new Size(_view.Resources.DisplayMetrics.WidthPixels, - _view.Resources.DisplayMetrics.HeightPixels); + RenderScaling = (int)_view.Resources.DisplayMetrics.Density; + + MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels, + _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); } private bool _handleEvents; @@ -60,19 +62,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IInputRoot InputRoot { get; private set; } - public virtual Size ClientSize - { - get - { - if (_view == null) - return new Size(0, 0); - return new Size(_view.Width, _view.Height); - } - set - { - - } - } + public virtual Size ClientSize => Size.ToSize(RenderScaling); public IMouseDevice MouseDevice { get; } = new MouseDevice(); @@ -113,12 +103,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform public Point PointToClient(PixelPoint point) { - return point.ToPoint(1); + return point.ToPoint(RenderScaling); } public PixelPoint PointToScreen(Point point) { - return PixelPoint.FromPoint(point, 1); + return PixelPoint.FromPoint(point, RenderScaling); } public void SetCursor(ICursorImpl cursor) @@ -136,7 +126,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _view.Visibility = ViewStates.Visible; } - public double RenderScaling => 1; + public double RenderScaling { get; } void Draw() { @@ -191,7 +181,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) { - var newSize = new Size(width, height); + var newSize = new PixelSize(width, height).ToSize(_tl.RenderScaling); if (newSize != _oldSize) { From 4ba8c4ebb76fc45e96a9bdb506edf6608590a4ad Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sun, 21 Mar 2021 19:03:13 +0700 Subject: [PATCH 4/4] Fix #5699: filter BuildAvaloniaApp by signature --- src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 764fc7b332..be2405efde 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -169,7 +169,7 @@ namespace Avalonia.DesignerSupport.Remote if (entryPoint == null) throw Die($"Assembly {args.AppPath} doesn't have an entry point"); var builderMethod = entryPoint.DeclaringType.GetMethod(BuilderMethodName, - BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Array.Empty(), null); if (builderMethod == null) throw Die($"{entryPoint.DeclaringType.FullName} doesn't have a method named {BuilderMethodName}"); Design.IsDesignMode = true;