AndroidKeyboardEventsHelper.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. using System;
  2. using System.Runtime.Versioning;
  3. using Android.Views;
  4. using Avalonia.Android.Platform.Input;
  5. using Avalonia.Android.Platform.SkiaPlatform;
  6. using Avalonia.Input;
  7. using Avalonia.Input.Raw;
  8. namespace Avalonia.Android.Platform.Specific.Helpers
  9. {
  10. internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl, IAndroidView
  11. {
  12. private readonly TView _view;
  13. public bool HandleEvents { get; set; }
  14. public AndroidKeyboardEventsHelper(TView view)
  15. {
  16. _view = view;
  17. HandleEvents = true;
  18. }
  19. public bool? DispatchKeyEvent(KeyEvent? e, out bool callBase)
  20. {
  21. if (!HandleEvents || e is null)
  22. {
  23. callBase = true;
  24. return null;
  25. }
  26. return DispatchKeyEventInternal(e, out callBase);
  27. }
  28. [ObsoletedOSPlatform("android29.0")]
  29. static string? UnicodeTextInput(KeyEvent keyEvent)
  30. {
  31. return keyEvent.Action == KeyEventActions.Multiple
  32. && keyEvent.RepeatCount == 0
  33. && !string.IsNullOrEmpty(keyEvent.Characters)
  34. ? keyEvent.Characters
  35. : null;
  36. }
  37. private bool? DispatchKeyEventInternal(KeyEvent e, out bool callBase)
  38. {
  39. var unicodeTextInput = OperatingSystem.IsAndroidVersionAtLeast(29) ? null : UnicodeTextInput(e);
  40. var inputRoot = _view.InputRoot;
  41. if ((e.Action == KeyEventActions.Multiple && unicodeTextInput == null)
  42. || inputRoot is null)
  43. {
  44. callBase = true;
  45. return null;
  46. }
  47. var physicalKey = AndroidKeyInterop.PhysicalKeyFromScanCode(e.ScanCode);
  48. var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey);
  49. var keyDeviceType = GetKeyDeviceType(e);
  50. var rawKeyEvent = new RawKeyEventArgs(
  51. AndroidKeyboardDevice.Instance!,
  52. Convert.ToUInt64(e.EventTime),
  53. inputRoot,
  54. e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
  55. AndroidKeyboardDevice.ConvertKey(e.KeyCode),
  56. GetModifierKeys(e),
  57. physicalKey,
  58. keyDeviceType,
  59. keySymbol);
  60. _view.Input?.Invoke(rawKeyEvent);
  61. bool handled = rawKeyEvent.Handled;
  62. if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
  63. || unicodeTextInput != null)
  64. {
  65. var rawTextEvent = new RawTextInputEventArgs(
  66. AndroidKeyboardDevice.Instance!,
  67. Convert.ToUInt64(e.EventTime),
  68. inputRoot,
  69. unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
  70. );
  71. _view.Input?.Invoke(rawTextEvent);
  72. handled = handled || rawTextEvent.Handled;
  73. }
  74. if (e.Action == KeyEventActions.Up)
  75. {
  76. //nothing to do here more call base no need of more events
  77. callBase = true;
  78. return null;
  79. }
  80. callBase = false;
  81. return handled;
  82. }
  83. private static RawInputModifiers GetModifierKeys(KeyEvent e)
  84. {
  85. var rv = RawInputModifiers.None;
  86. if (e.IsCtrlPressed) rv |= RawInputModifiers.Control;
  87. if (e.IsShiftPressed) rv |= RawInputModifiers.Shift;
  88. return rv;
  89. }
  90. private static string? GetKeySymbol(int unicodeChar, PhysicalKey physicalKey)
  91. {
  92. // Handle a very limited set of control characters so that we're consistent with other platforms
  93. // (matches KeySymbolHelper.IsAllowedAsciiKeySymbol)
  94. switch (physicalKey)
  95. {
  96. case PhysicalKey.Backspace:
  97. return "\b";
  98. case PhysicalKey.Tab:
  99. return "\t";
  100. case PhysicalKey.Enter:
  101. case PhysicalKey.NumPadEnter:
  102. return "\r";
  103. case PhysicalKey.Escape:
  104. return "\u001B";
  105. default:
  106. if (unicodeChar <= 0x7F)
  107. {
  108. var asciiChar = (char)unicodeChar;
  109. return KeySymbolHelper.IsAllowedAsciiKeySymbol(asciiChar) ? asciiChar.ToString() : null;
  110. }
  111. return char.ConvertFromUtf32(unicodeChar);
  112. }
  113. }
  114. private KeyDeviceType GetKeyDeviceType(KeyEvent e)
  115. {
  116. var source = e.Device?.Sources ?? InputSourceType.Unknown;
  117. // Remote controller reports itself as "DPad | Keyboard", which is confusing,
  118. // so we need to double-check KeyboardType as well.
  119. if (source.HasAnyFlag(InputSourceType.Dpad)
  120. && e.Device?.KeyboardType == InputKeyboardType.NonAlphabetic)
  121. return KeyDeviceType.Remote;
  122. // ReSharper disable BitwiseOperatorOnEnumWithoutFlags - it IS flags enum under the hood.
  123. if (source.HasAnyFlag(InputSourceType.Joystick | InputSourceType.Gamepad))
  124. return KeyDeviceType.Gamepad;
  125. // ReSharper restore BitwiseOperatorOnEnumWithoutFlags
  126. return KeyDeviceType.Keyboard; // fallback to the keyboard, if unknown.
  127. }
  128. public void Dispose()
  129. {
  130. HandleEvents = false;
  131. }
  132. }
  133. }