A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

159 lines
5.5 KiB

using System;
using System.Runtime.Versioning;
using Android.Views;
using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Input;
using Avalonia.Input.Raw;
namespace Avalonia.Android.Platform.Specific.Helpers
{
internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl, IAndroidView
{
private readonly TView _view;
public bool HandleEvents { get; set; }
public AndroidKeyboardEventsHelper(TView view)
{
_view = view;
HandleEvents = true;
}
public bool? DispatchKeyEvent(KeyEvent? e, out bool callBase)
{
if (!HandleEvents || e is null)
{
callBase = true;
return null;
}
return DispatchKeyEventInternal(e, out callBase);
}
[ObsoletedOSPlatform("android29.0")]
static string? UnicodeTextInput(KeyEvent keyEvent)
{
return keyEvent.Action == KeyEventActions.Multiple
&& keyEvent.RepeatCount == 0
&& !string.IsNullOrEmpty(keyEvent.Characters)
? keyEvent.Characters
: null;
}
private bool? DispatchKeyEventInternal(KeyEvent e, out bool callBase)
{
var unicodeTextInput = OperatingSystem.IsAndroidVersionAtLeast(29) ? null : UnicodeTextInput(e);
var inputRoot = _view.InputRoot;
if ((e.Action == KeyEventActions.Multiple && unicodeTextInput == null)
|| inputRoot is null)
{
callBase = true;
return null;
}
var physicalKey = AndroidKeyInterop.PhysicalKeyFromScanCode(e.ScanCode);
var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey);
var keyDeviceType = GetKeyDeviceType(e);
var rawKeyEvent = new RawKeyEventArgs(
AndroidKeyboardDevice.Instance!,
Convert.ToUInt64(e.EventTime),
inputRoot,
e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
AndroidKeyboardDevice.ConvertKey(e.KeyCode),
GetModifierKeys(e),
physicalKey,
keyDeviceType,
keySymbol);
_view.Input?.Invoke(rawKeyEvent);
bool handled = rawKeyEvent.Handled;
if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
|| unicodeTextInput != null)
{
var rawTextEvent = new RawTextInputEventArgs(
AndroidKeyboardDevice.Instance!,
Convert.ToUInt64(e.EventTime),
inputRoot,
unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
);
_view.Input?.Invoke(rawTextEvent);
handled = handled || rawTextEvent.Handled;
}
if (e.Action == KeyEventActions.Up)
{
//nothing to do here more call base no need of more events
callBase = true;
return null;
}
callBase = false;
return handled;
}
private static RawInputModifiers GetModifierKeys(KeyEvent e)
{
var rv = RawInputModifiers.None;
if (e.IsCtrlPressed) rv |= RawInputModifiers.Control;
if (e.IsShiftPressed) rv |= RawInputModifiers.Shift;
return rv;
}
private static string? GetKeySymbol(int unicodeChar, PhysicalKey physicalKey)
{
// Handle a very limited set of control characters so that we're consistent with other platforms
// (matches KeySymbolHelper.IsAllowedAsciiKeySymbol)
switch (physicalKey)
{
case PhysicalKey.Backspace:
return "\b";
case PhysicalKey.Tab:
return "\t";
case PhysicalKey.Enter:
case PhysicalKey.NumPadEnter:
return "\r";
case PhysicalKey.Escape:
return "\u001B";
default:
if (unicodeChar <= 0x7F)
{
var asciiChar = (char)unicodeChar;
return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? asciiChar.ToString() : null;
}
return char.ConvertFromUtf32(unicodeChar);
}
}
private KeyDeviceType GetKeyDeviceType(KeyEvent e)
{
var source = e.Device?.Sources ?? InputSourceType.Unknown;
// Remote controller reports itself as "DPad | Keyboard", which is confusing,
// so we need to double-check KeyboardType as well.
if (source.HasAnyFlag(InputSourceType.Dpad)
&& e.Device?.KeyboardType == InputKeyboardType.NonAlphabetic)
return KeyDeviceType.Remote;
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags - it IS flags enum under the hood.
if (source.HasAnyFlag(InputSourceType.Joystick | InputSourceType.Gamepad))
return KeyDeviceType.Gamepad;
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
return KeyDeviceType.Keyboard; // fallback to the keyboard, if unknown.
}
public void Dispose()
{
HandleEvents = false;
}
}
}