Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added PhysicalKey handling for Android #13006

Merged
merged 1 commit into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#nullable enable

using System.Collections.Generic;
using Avalonia.Input;

namespace Avalonia.Android.Platform.Specific.Helpers;

internal static class AndroidKeyInterop
{
// evdev scan code to physical key map.
// https://github.com/chromium/chromium/blob/main/ui/events/keycodes/dom/dom_code_data.inc
// This list has the same order as the PhysicalKey enum.{
private static readonly Dictionary<byte, PhysicalKey> s_physicalKeyFromScanCode = new(0xA2)
{
// Writing System Keys
{ 0x29, PhysicalKey.Backquote },
{ 0x2B, PhysicalKey.Backslash },
{ 0x1A, PhysicalKey.BracketLeft },
{ 0x1B, PhysicalKey.BracketRight },
{ 0x33, PhysicalKey.Comma },
{ 0x0B, PhysicalKey.Digit0 },
{ 0x02, PhysicalKey.Digit1 },
{ 0x03, PhysicalKey.Digit2 },
{ 0x04, PhysicalKey.Digit3 },
{ 0x05, PhysicalKey.Digit4 },
{ 0x06, PhysicalKey.Digit5 },
{ 0x07, PhysicalKey.Digit6 },
{ 0x08, PhysicalKey.Digit7 },
{ 0x09, PhysicalKey.Digit8 },
{ 0x0A, PhysicalKey.Digit9 },
{ 0x0D, PhysicalKey.Equal },
{ 0x56, PhysicalKey.IntlBackslash },
{ 0x59, PhysicalKey.IntlRo },
{ 0x7C, PhysicalKey.IntlYen },
{ 0x1E, PhysicalKey.A },
{ 0x30, PhysicalKey.B },
{ 0x2E, PhysicalKey.C },
{ 0x20, PhysicalKey.D },
{ 0x12, PhysicalKey.E },
{ 0x21, PhysicalKey.F },
{ 0x22, PhysicalKey.G },
{ 0x23, PhysicalKey.H },
{ 0x17, PhysicalKey.I },
{ 0x24, PhysicalKey.J },
{ 0x25, PhysicalKey.K },
{ 0x26, PhysicalKey.L },
{ 0x32, PhysicalKey.M },
{ 0x31, PhysicalKey.N },
{ 0x18, PhysicalKey.O },
{ 0x19, PhysicalKey.P },
{ 0x10, PhysicalKey.Q },
{ 0x13, PhysicalKey.R },
{ 0x1F, PhysicalKey.S },
{ 0x14, PhysicalKey.T },
{ 0x16, PhysicalKey.U },
{ 0x2F, PhysicalKey.V },
{ 0x11, PhysicalKey.W },
{ 0x2D, PhysicalKey.X },
{ 0x15, PhysicalKey.Y },
{ 0x2C, PhysicalKey.Z },
{ 0x0C, PhysicalKey.Minus },
{ 0x34, PhysicalKey.Period },
{ 0x28, PhysicalKey.Quote },
{ 0x27, PhysicalKey.Semicolon },
{ 0x35, PhysicalKey.Slash },

// Functional Keys
{ 0x38, PhysicalKey.AltLeft },
{ 0x64, PhysicalKey.AltRight },
{ 0x0E, PhysicalKey.Backspace },
{ 0x3A, PhysicalKey.CapsLock },
{ 0x7F, PhysicalKey.ContextMenu },
{ 0x1D, PhysicalKey.ControlLeft },
{ 0x61, PhysicalKey.ControlRight },
{ 0x1C, PhysicalKey.Enter },
{ 0x7D, PhysicalKey.MetaLeft },
{ 0x7E, PhysicalKey.MetaRight },
{ 0x2A, PhysicalKey.ShiftLeft },
{ 0x36, PhysicalKey.ShiftRight },
{ 0x39, PhysicalKey.Space },
{ 0x0F, PhysicalKey.Tab },
{ 0x5C, PhysicalKey.Convert },
{ 0x5D, PhysicalKey.KanaMode },
{ 0x7A, PhysicalKey.Lang1 },
{ 0x7B, PhysicalKey.Lang2 },
{ 0x5A, PhysicalKey.Lang3 },
{ 0x5B, PhysicalKey.Lang4 },
{ 0x55, PhysicalKey.Lang5 },
{ 0x5E, PhysicalKey.NonConvert },

// Control Pad Section
{ 0x6F, PhysicalKey.Delete },
{ 0x6B, PhysicalKey.End },
{ 0x8A, PhysicalKey.Help },
{ 0x66, PhysicalKey.Home },
{ 0x6E, PhysicalKey.Insert },
{ 0x6D, PhysicalKey.PageDown },
{ 0x68, PhysicalKey.PageUp },

// Arrow Pad Section
{ 0x6C, PhysicalKey.ArrowDown },
{ 0x69, PhysicalKey.ArrowLeft },
{ 0x6A, PhysicalKey.ArrowRight },
{ 0x67, PhysicalKey.ArrowUp },

// Numpad Section
{ 0x45, PhysicalKey.NumLock },
{ 0x52, PhysicalKey.NumPad0 },
{ 0x4F, PhysicalKey.NumPad1 },
{ 0x50, PhysicalKey.NumPad2 },
{ 0x51, PhysicalKey.NumPad3 },
{ 0x4B, PhysicalKey.NumPad4 },
{ 0x4C, PhysicalKey.NumPad5 },
{ 0x4D, PhysicalKey.NumPad6 },
{ 0x47, PhysicalKey.NumPad7 },
{ 0x48, PhysicalKey.NumPad8 },
{ 0x49, PhysicalKey.NumPad9 },
{ 0x4E, PhysicalKey.NumPadAdd },
//{ , PhysicalKey.NumPadClear },
{ 0x79, PhysicalKey.NumPadComma },
{ 0x53, PhysicalKey.NumPadDecimal },
{ 0x62, PhysicalKey.NumPadDivide },
{ 0x60, PhysicalKey.NumPadEnter },
{ 0x75, PhysicalKey.NumPadEqual },
{ 0x37, PhysicalKey.NumPadMultiply },
{ 0xB3, PhysicalKey.NumPadParenLeft },
{ 0xB4, PhysicalKey.NumPadParenRight },
{ 0x4A, PhysicalKey.NumPadSubtract },

// Function Section
{ 0x01, PhysicalKey.Escape },
{ 0x3B, PhysicalKey.F1 },
{ 0x3C, PhysicalKey.F2 },
{ 0x3D, PhysicalKey.F3 },
{ 0x3E, PhysicalKey.F4 },
{ 0x3F, PhysicalKey.F5 },
{ 0x40, PhysicalKey.F6 },
{ 0x41, PhysicalKey.F7 },
{ 0x42, PhysicalKey.F8 },
{ 0x43, PhysicalKey.F9 },
{ 0x44, PhysicalKey.F10 },
{ 0x57, PhysicalKey.F11 },
{ 0x58, PhysicalKey.F12 },
{ 0xB7, PhysicalKey.F13 },
{ 0xB8, PhysicalKey.F14 },
{ 0xB9, PhysicalKey.F15 },
{ 0xBA, PhysicalKey.F16 },
{ 0xBB, PhysicalKey.F17 },
{ 0xBC, PhysicalKey.F18 },
{ 0xBD, PhysicalKey.F19 },
{ 0xBE, PhysicalKey.F20 },
{ 0xBF, PhysicalKey.F21 },
{ 0xC0, PhysicalKey.F22 },
{ 0xC1, PhysicalKey.F23 },
{ 0xC2, PhysicalKey.F24 },
{ 0x63, PhysicalKey.PrintScreen },
{ 0x46, PhysicalKey.ScrollLock },
{ 0x77, PhysicalKey.Pause },

// Media Keys
{ 0x9E, PhysicalKey.BrowserBack },
{ 0x9C, PhysicalKey.BrowserFavorites },
{ 0x9F, PhysicalKey.BrowserForward },
{ 0xAC, PhysicalKey.BrowserHome },
{ 0xAD, PhysicalKey.BrowserRefresh },
{ 0xD9, PhysicalKey.BrowserSearch },
{ 0x80, PhysicalKey.BrowserStop },
{ 0xA1, PhysicalKey.Eject },
{ 0x90, PhysicalKey.LaunchApp1 },
{ 0x8C, PhysicalKey.LaunchApp2 },
{ 0x9B, PhysicalKey.LaunchMail },
{ 0xA4, PhysicalKey.MediaPlayPause },
{ 0xAB, PhysicalKey.MediaSelect },
{ 0xA6, PhysicalKey.MediaStop },
{ 0xA3, PhysicalKey.MediaTrackNext },
{ 0xA5, PhysicalKey.MediaTrackPrevious },
{ 0x74, PhysicalKey.Power },
{ 0x8E, PhysicalKey.Sleep },
{ 0x72, PhysicalKey.AudioVolumeDown },
{ 0x71, PhysicalKey.AudioVolumeMute },
{ 0x73, PhysicalKey.AudioVolumeUp },
{ 0x8F, PhysicalKey.WakeUp },

// Legacy Keys
{ 0x81, PhysicalKey.Again },
{ 0x85, PhysicalKey.Copy },
{ 0x89, PhysicalKey.Cut },
{ 0x88, PhysicalKey.Find },
{ 0x86, PhysicalKey.Open },
{ 0x87, PhysicalKey.Paste },
//{ , PhysicalKey.Props },
{ 0x84, PhysicalKey.Select },
{ 0x83, PhysicalKey.Undo }
};

public static PhysicalKey PhysicalKeyFromScanCode(int scanCode)
=> scanCode is > 0 and <= 255 && s_physicalKeyFromScanCode.TryGetValue((byte)scanCode, out var result) ?
result :
PhysicalKey.None;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable

using System;
using Android.Views;
using Avalonia.Android.Platform.Input;
Expand Down Expand Up @@ -30,11 +32,11 @@ public AndroidKeyboardEventsHelper(TView view)
return DispatchKeyEventInternal(e, out callBase);
}

static string UnicodeTextInput(KeyEvent keyEvent)
static string? UnicodeTextInput(KeyEvent keyEvent)
{
return keyEvent.Action == KeyEventActions.Multiple
&& keyEvent.RepeatCount == 0
&& !string.IsNullOrEmpty(keyEvent?.Characters)
&& !string.IsNullOrEmpty(keyEvent.Characters)
? keyEvent.Characters
: null;
}
Expand All @@ -49,15 +51,18 @@ static string UnicodeTextInput(KeyEvent keyEvent)
return null;
}

var physicalKey = AndroidKeyInterop.PhysicalKeyFromScanCode(e.ScanCode);
var keySymbol = GetKeySymbol(e.UnicodeChar, physicalKey);

var rawKeyEvent = new RawKeyEventArgs(
AndroidKeyboardDevice.Instance,
Convert.ToUInt64(e.EventTime),
_view.InputRoot,
e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
AndroidKeyboardDevice.ConvertKey(e.KeyCode),
GetModifierKeys(e),
PhysicalKey.None,
e.DisplayLabel == '\0' ? null : new string(e.DisplayLabel, 1));
physicalKey,
keySymbol);

_view.Input(rawKeyEvent);

Expand Down Expand Up @@ -94,6 +99,31 @@ private static RawInputModifiers GetModifierKeys(KeyEvent e)
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);
}
}

public void Dispose()
{
HandleEvents = false;
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Input/KeySymbolHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static bool IsAllowedAsciiKeySymbol(char c)
}
}

if (c == 0x07) // delete
if (c == 0x7F) // delete
return false;

return true;
Expand Down