diff --git a/src/Android/Avalonia.Android/AndroidDispatcherImpl.cs b/src/Android/Avalonia.Android/AndroidDispatcherImpl.cs index 8ee5f2a8f0..ea23b64a42 100644 --- a/src/Android/Avalonia.Android/AndroidDispatcherImpl.cs +++ b/src/Android/Avalonia.Android/AndroidDispatcherImpl.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Threading; using Android.OS; using Avalonia.Controls.Documents; using Avalonia.Threading; @@ -19,8 +20,7 @@ namespace Avalonia.Android private readonly Runnable _timerSignaler; private readonly Runnable _wakeupSignaler; private readonly MessageQueue _queue; - private readonly object _lock = new(); - private bool _signaled; + private int _signaled; private bool _backgroundProcessingRequested; @@ -46,8 +46,7 @@ namespace Avalonia.Android public event Action? Signaled; private void OnSignaled() { - lock (_lock) - _signaled = false; + Interlocked.Exchange(ref _signaled, 0); Signaled?.Invoke(); } @@ -68,13 +67,11 @@ namespace Avalonia.Android public void Signal() { - lock (_lock) + if (Interlocked.CompareExchange(ref _signaled, 1, 0) != 0) { - if(_signaled) - return; - _signaled = true; - _handler.Post(_signaler); + return; } + _handler.Post(_signaler); } readonly Stopwatch _clock = Stopwatch.StartNew(); @@ -133,11 +130,9 @@ namespace Avalonia.Android // "background" jobs not being processed // So we need to examine the queue state to prevent that scenario - lock (_lock) + if (Volatile.Read(ref _signaled) != 0) { - // There are higher priority jobs enqueued, we'll be called again - if (_signaled) - return; + return; } if (CanQueryPendingInput) diff --git a/src/Android/Avalonia.Android/AvaloniaAccessHelper.cs b/src/Android/Avalonia.Android/AvaloniaAccessHelper.cs index ca06998dbc..6d5128ece2 100644 --- a/src/Android/Avalonia.Android/AvaloniaAccessHelper.cs +++ b/src/Android/Avalonia.Android/AvaloniaAccessHelper.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; + using Android.OS; using AndroidX.Core.View.Accessibility; using AndroidX.CustomView.Widget; @@ -134,9 +134,18 @@ namespace Avalonia.Android protected override bool OnPerformActionForVirtualView(int virtualViewId, int action, Bundle? arguments) { - return (GetNodeInfoProvidersFromVirtualViewId(virtualViewId) ?? []) - .Select(x => TryPerformNodeAction(x, action, arguments)) - .Aggregate(false, (a, b) => a | b); + var providers = GetNodeInfoProvidersFromVirtualViewId(virtualViewId); + if (providers == null) + { + return false; + } + + var result = false; + foreach (var provider in providers) + { + result |= TryPerformNodeAction(provider, action, arguments); + } + return result; } private static bool TryPerformNodeAction(INodeInfoProvider nodeInfoProvider, int action, Bundle? arguments) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 20284906be..da128cec96 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -4,6 +4,7 @@ using Android.App; using Android.Content; using Android.Graphics; using Android.Graphics.Drawables; +using Android.OS; using Android.Runtime; using Android.Views; using AndroidX.AppCompat.App; @@ -144,6 +145,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly TopLevelImpl _tl; private Size _oldSize; private double _oldScaling; + private Paint? _clearPaint; public SurfaceViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context) { @@ -159,11 +161,13 @@ namespace Avalonia.Android.Platform.SkiaPlatform // can be seen below, but it does not. if (OperatingSystem.IsAndroidVersionAtLeast(29)) { - // Android 10+ does this (BlendMode was new) - var paint = new Paint(); - paint.SetColor(0); - paint.BlendMode = BlendMode.Clear; - canvas.DrawRect(0, 0, Width, Height, paint); + if (_clearPaint == null) + { + _clearPaint = new Paint(); + _clearPaint.SetColor(0); + _clearPaint.BlendMode = BlendMode.Clear; + } + canvas.DrawRect(0, 0, Width, Height, _clearPaint); } else { @@ -384,7 +388,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform { if(Input != null) { - var args = new RawTextInputEventArgs(AndroidKeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, InputRoot!, text); + var args = new RawTextInputEventArgs(AndroidKeyboardDevice.Instance!, (ulong)SystemClock.UptimeMillis(), InputRoot!, text); Input(args); } diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 03be7c2153..59b1bc9899 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -10,6 +10,18 @@ namespace Avalonia.Android.Platform.Specific.Helpers { internal class AndroidKeyboardEventsHelper : IDisposable where TView : TopLevelImpl { + private static readonly string[] s_asciiStringCache = InitAsciiStringCache(); + + private static string[] InitAsciiStringCache() + { + var cache = new string[128]; + for (int i = 0; i < 128; i++) + { + cache[i] = ((char)i).ToString(); + } + return cache; + } + private readonly TView _view; public bool HandleEvents { get; set; } @@ -79,7 +91,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers AndroidKeyboardDevice.Instance!, Convert.ToUInt64(e.EventTime), inputRoot, - unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString() + unicodeTextInput ?? CharToString(e.UnicodeChar) ); _view.Input?.Invoke(rawTextEvent); @@ -107,6 +119,15 @@ namespace Avalonia.Android.Platform.Specific.Helpers return rv; } + private static string CharToString(int unicodeChar) + { + if (unicodeChar >= 0 && unicodeChar < s_asciiStringCache.Length) + { + return s_asciiStringCache[unicodeChar]; + } + return char.ConvertFromUtf32(unicodeChar); + } + private static string? GetKeySymbol(int unicodeChar, PhysicalKey physicalKey) { // Handle a very limited set of control characters so that we're consistent with other platforms @@ -126,7 +147,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers if (unicodeChar <= 0x7F) { var asciiChar = (char)unicodeChar; - return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? asciiChar.ToString() : null; + return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? s_asciiStringCache[asciiChar] : null; } return char.ConvertFromUtf32(unicodeChar); }