Unverified Commit ef9c059e authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Hook up character events and unmodified code points to Android raw key event handling. (#27853)

This makes Android raw key event handling use the "character" data coming from the engine properly, and gets rid of the "toLowerCase" hack that I was using to normalize logical key events, in favor of using the new "plainCodePoint" that has the unmodified code point (the code point as if no modifier keys were pressed).
parent 8d0346a0
......@@ -79,7 +79,11 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
Text('modifiers set: $modifierList'),
];
if (data is RawKeyEventDataAndroid) {
dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)})'));
const int combiningCharacterMask = 0x7fffffff;
final String codePointChar = String.fromCharCode(combiningCharacterMask & data.codePoint);
dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)}: $codePointChar)'));
final String plainCodePointChar = String.fromCharCode(combiningCharacterMask & data.plainCodePoint);
dataText.add(Text('plainCodePoint: ${data.plainCodePoint} (${_asHex(data.plainCodePoint)}: $plainCodePointChar)'));
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode} (${_asHex(data.scanCode)})'));
dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})'));
......@@ -91,6 +95,9 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
if (_event.character != null) {
dataText.add(Text('character: ${_event.character}'));
}
for (ModifierKey modifier in data.modifiersPressed.keys) {
for (KeyboardSide side in KeyboardSide.values) {
if (data.isModifierPressed(modifier, side: side)) {
......
......@@ -236,7 +236,7 @@ abstract class RawKeyEvent {
/// const subclasses.
const RawKeyEvent({
@required this.data,
@required this.character,
this.character,
});
/// Creates a concrete [RawKeyEvent] class from a message in the form received
......@@ -245,13 +245,13 @@ abstract class RawKeyEvent {
RawKeyEventData data;
final String keymap = message['keymap'];
final String character = message['character'];
switch (keymap) {
case 'android':
data = RawKeyEventDataAndroid(
flags: message['flags'] ?? 0,
codePoint: message['codePoint'] ?? 0,
keyCode: message['keyCode'] ?? 0,
plainCodePoint: message['plainCodePoint'] ?? 0,
scanCode: message['scanCode'] ?? 0,
metaState: message['metaState'] ?? 0,
);
......@@ -273,9 +273,9 @@ abstract class RawKeyEvent {
final String type = message['type'];
switch (type) {
case 'keydown':
return RawKeyDownEvent(data: data, character: character);
return RawKeyDownEvent(data: data, character: message['character']);
case 'keyup':
return RawKeyUpEvent(data: data, character: character);
return RawKeyUpEvent(data: data);
default:
throw FlutterError('Unknown key event type: $type');
}
......@@ -399,7 +399,7 @@ class RawKeyDownEvent extends RawKeyEvent {
/// Creates a key event that represents the user pressing a key.
const RawKeyDownEvent({
@required RawKeyEventData data,
@required String character,
String character,
}) : super(data: data, character: character);
}
......@@ -412,7 +412,7 @@ class RawKeyUpEvent extends RawKeyEvent {
/// Creates a key event that represents the user releasing a key.
const RawKeyUpEvent({
@required RawKeyEventData data,
@required String character,
String character,
}) : super(data: data, character: character);
}
......
......@@ -29,6 +29,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
const RawKeyEventDataAndroid({
this.flags = 0,
this.codePoint = 0,
this.plainCodePoint = 0,
this.keyCode = 0,
this.scanCode = 0,
this.metaState = 0,
......@@ -56,6 +57,19 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
/// for more information.
final int codePoint;
/// The Unicode code point represented by the key event, if any, without
/// regard to any modifier keys which are currently pressed.
///
/// If there is no Unicode code point, this value is zero.
///
/// Dead keys are represented as Unicode combining characters.
///
/// This is the result of calling KeyEvent.getUnicodeChar(0) on Android.
///
/// See <https://developer.android.com/reference/android/view/KeyEvent.html#getUnicodeChar(int)>
/// for more information.
final int plainCodePoint;
/// The hardware key code corresponding to this key event.
///
/// This is the physical key that was pressed, not the Unicode character.
......@@ -93,15 +107,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
// Android only reports a single code point for the key label.
@override
String get keyLabel => codePoint == 0 ? null : String.fromCharCode(_combinedCodePoint).toLowerCase();
// Handles the logic for removing Android's "combining character" flag on the
// codePoint.
int get _combinedCodePoint => codePoint & _kCombiningCharacterMask;
// In order for letter keys to match the corresponding constant, they need to
// be a consistent case.
int get _lowercaseCodePoint => String.fromCharCode(_combinedCodePoint).toLowerCase().codeUnitAt(0);
String get keyLabel => plainCodePoint == 0 ? null : String.fromCharCode(plainCodePoint & _kCombiningCharacterMask);
@override
PhysicalKeyboardKey get physicalKey => kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
......@@ -121,7 +127,8 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
// autogenerated, since the label uniquely identifies an ID from the Unicode
// plane.
if (keyLabel != null && keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) {
final int keyId = LogicalKeyboardKey.unicodePlane | (_lowercaseCodePoint & LogicalKeyboardKey.valueMask);
final int combinedCodePoint = plainCodePoint & _kCombiningCharacterMask;
final int keyId = LogicalKeyboardKey.unicodePlane | (combinedCodePoint & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
......
......@@ -35,8 +35,9 @@ void main() {
'type': 'keydown',
'keymap': 'android',
'keyCode': 0x04,
'codePoint': 0x64,
'scanCode': 0x64,
'plainCodePoint': 0x64,
'codePoint': 0x44,
'scanCode': 0x20,
'metaState': modifier,
});
final RawKeyEventDataAndroid data = event.data;
......@@ -68,8 +69,9 @@ void main() {
'type': 'keydown',
'keymap': 'android',
'keyCode': 0x04,
'codePoint': 0x64,
'scanCode': 0x64,
'plainCodePoint': 0x64,
'codePoint': 0x44,
'scanCode': 0x20,
'metaState': modifier | RawKeyEventDataAndroid.modifierFunction,
});
final RawKeyEventDataAndroid data = event.data;
......@@ -102,6 +104,7 @@ void main() {
'type': 'keydown',
'keymap': 'android',
'keyCode': 29,
'plainCodePoint': 'a'.codeUnitAt(0),
'codePoint': 'A'.codeUnitAt(0),
'character': 'A',
'scanCode': 30,
......@@ -117,7 +120,7 @@ void main() {
'type': 'keydown',
'keymap': 'android',
'keyCode': 111,
'codePoint': null,
'codePoint': 0,
'character': null,
'scanCode': 1,
'metaState': 0x0,
......@@ -132,7 +135,8 @@ void main() {
'type': 'keydown',
'keymap': 'android',
'keyCode': 59,
'codePoint': null,
'plainCodePoint': 0,
'codePoint': 0,
'character': null,
'scanCode': 42,
'metaState': RawKeyEventDataAndroid.modifierLeftShift,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment