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> { ...@@ -79,7 +79,11 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
Text('modifiers set: $modifierList'), Text('modifiers set: $modifierList'),
]; ];
if (data is RawKeyEventDataAndroid) { 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('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode} (${_asHex(data.scanCode)})')); dataText.add(Text('scanCode: ${data.scanCode} (${_asHex(data.scanCode)})'));
dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})')); dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})'));
...@@ -91,6 +95,9 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> { ...@@ -91,6 +95,9 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
} }
dataText.add(Text('logical: ${_event.logicalKey}')); dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}')); dataText.add(Text('physical: ${_event.physicalKey}'));
if (_event.character != null) {
dataText.add(Text('character: ${_event.character}'));
}
for (ModifierKey modifier in data.modifiersPressed.keys) { for (ModifierKey modifier in data.modifiersPressed.keys) {
for (KeyboardSide side in KeyboardSide.values) { for (KeyboardSide side in KeyboardSide.values) {
if (data.isModifierPressed(modifier, side: side)) { if (data.isModifierPressed(modifier, side: side)) {
......
...@@ -236,7 +236,7 @@ abstract class RawKeyEvent { ...@@ -236,7 +236,7 @@ abstract class RawKeyEvent {
/// const subclasses. /// const subclasses.
const RawKeyEvent({ const RawKeyEvent({
@required this.data, @required this.data,
@required this.character, this.character,
}); });
/// Creates a concrete [RawKeyEvent] class from a message in the form received /// Creates a concrete [RawKeyEvent] class from a message in the form received
...@@ -245,13 +245,13 @@ abstract class RawKeyEvent { ...@@ -245,13 +245,13 @@ abstract class RawKeyEvent {
RawKeyEventData data; RawKeyEventData data;
final String keymap = message['keymap']; final String keymap = message['keymap'];
final String character = message['character'];
switch (keymap) { switch (keymap) {
case 'android': case 'android':
data = RawKeyEventDataAndroid( data = RawKeyEventDataAndroid(
flags: message['flags'] ?? 0, flags: message['flags'] ?? 0,
codePoint: message['codePoint'] ?? 0, codePoint: message['codePoint'] ?? 0,
keyCode: message['keyCode'] ?? 0, keyCode: message['keyCode'] ?? 0,
plainCodePoint: message['plainCodePoint'] ?? 0,
scanCode: message['scanCode'] ?? 0, scanCode: message['scanCode'] ?? 0,
metaState: message['metaState'] ?? 0, metaState: message['metaState'] ?? 0,
); );
...@@ -273,9 +273,9 @@ abstract class RawKeyEvent { ...@@ -273,9 +273,9 @@ abstract class RawKeyEvent {
final String type = message['type']; final String type = message['type'];
switch (type) { switch (type) {
case 'keydown': case 'keydown':
return RawKeyDownEvent(data: data, character: character); return RawKeyDownEvent(data: data, character: message['character']);
case 'keyup': case 'keyup':
return RawKeyUpEvent(data: data, character: character); return RawKeyUpEvent(data: data);
default: default:
throw FlutterError('Unknown key event type: $type'); throw FlutterError('Unknown key event type: $type');
} }
...@@ -399,7 +399,7 @@ class RawKeyDownEvent extends RawKeyEvent { ...@@ -399,7 +399,7 @@ class RawKeyDownEvent extends RawKeyEvent {
/// Creates a key event that represents the user pressing a key. /// Creates a key event that represents the user pressing a key.
const RawKeyDownEvent({ const RawKeyDownEvent({
@required RawKeyEventData data, @required RawKeyEventData data,
@required String character, String character,
}) : super(data: data, character: character); }) : super(data: data, character: character);
} }
...@@ -412,7 +412,7 @@ class RawKeyUpEvent extends RawKeyEvent { ...@@ -412,7 +412,7 @@ class RawKeyUpEvent extends RawKeyEvent {
/// Creates a key event that represents the user releasing a key. /// Creates a key event that represents the user releasing a key.
const RawKeyUpEvent({ const RawKeyUpEvent({
@required RawKeyEventData data, @required RawKeyEventData data,
@required String character, String character,
}) : super(data: data, character: character); }) : super(data: data, character: character);
} }
......
...@@ -29,6 +29,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData { ...@@ -29,6 +29,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
const RawKeyEventDataAndroid({ const RawKeyEventDataAndroid({
this.flags = 0, this.flags = 0,
this.codePoint = 0, this.codePoint = 0,
this.plainCodePoint = 0,
this.keyCode = 0, this.keyCode = 0,
this.scanCode = 0, this.scanCode = 0,
this.metaState = 0, this.metaState = 0,
...@@ -56,6 +57,19 @@ class RawKeyEventDataAndroid extends RawKeyEventData { ...@@ -56,6 +57,19 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
/// for more information. /// for more information.
final int codePoint; 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. /// The hardware key code corresponding to this key event.
/// ///
/// This is the physical key that was pressed, not the Unicode character. /// This is the physical key that was pressed, not the Unicode character.
...@@ -93,15 +107,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData { ...@@ -93,15 +107,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
// Android only reports a single code point for the key label. // Android only reports a single code point for the key label.
@override @override
String get keyLabel => codePoint == 0 ? null : String.fromCharCode(_combinedCodePoint).toLowerCase(); String get keyLabel => plainCodePoint == 0 ? null : String.fromCharCode(plainCodePoint & _kCombiningCharacterMask);
// 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);
@override @override
PhysicalKeyboardKey get physicalKey => kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none; PhysicalKeyboardKey get physicalKey => kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
...@@ -121,7 +127,8 @@ class RawKeyEventDataAndroid extends RawKeyEventData { ...@@ -121,7 +127,8 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
// autogenerated, since the label uniquely identifies an ID from the Unicode // autogenerated, since the label uniquely identifies an ID from the Unicode
// plane. // plane.
if (keyLabel != null && keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) { 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( return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId, keyId,
keyLabel: keyLabel, keyLabel: keyLabel,
......
...@@ -35,8 +35,9 @@ void main() { ...@@ -35,8 +35,9 @@ void main() {
'type': 'keydown', 'type': 'keydown',
'keymap': 'android', 'keymap': 'android',
'keyCode': 0x04, 'keyCode': 0x04,
'codePoint': 0x64, 'plainCodePoint': 0x64,
'scanCode': 0x64, 'codePoint': 0x44,
'scanCode': 0x20,
'metaState': modifier, 'metaState': modifier,
}); });
final RawKeyEventDataAndroid data = event.data; final RawKeyEventDataAndroid data = event.data;
...@@ -68,8 +69,9 @@ void main() { ...@@ -68,8 +69,9 @@ void main() {
'type': 'keydown', 'type': 'keydown',
'keymap': 'android', 'keymap': 'android',
'keyCode': 0x04, 'keyCode': 0x04,
'codePoint': 0x64, 'plainCodePoint': 0x64,
'scanCode': 0x64, 'codePoint': 0x44,
'scanCode': 0x20,
'metaState': modifier | RawKeyEventDataAndroid.modifierFunction, 'metaState': modifier | RawKeyEventDataAndroid.modifierFunction,
}); });
final RawKeyEventDataAndroid data = event.data; final RawKeyEventDataAndroid data = event.data;
...@@ -102,6 +104,7 @@ void main() { ...@@ -102,6 +104,7 @@ void main() {
'type': 'keydown', 'type': 'keydown',
'keymap': 'android', 'keymap': 'android',
'keyCode': 29, 'keyCode': 29,
'plainCodePoint': 'a'.codeUnitAt(0),
'codePoint': 'A'.codeUnitAt(0), 'codePoint': 'A'.codeUnitAt(0),
'character': 'A', 'character': 'A',
'scanCode': 30, 'scanCode': 30,
...@@ -117,7 +120,7 @@ void main() { ...@@ -117,7 +120,7 @@ void main() {
'type': 'keydown', 'type': 'keydown',
'keymap': 'android', 'keymap': 'android',
'keyCode': 111, 'keyCode': 111,
'codePoint': null, 'codePoint': 0,
'character': null, 'character': null,
'scanCode': 1, 'scanCode': 1,
'metaState': 0x0, 'metaState': 0x0,
...@@ -132,7 +135,8 @@ void main() { ...@@ -132,7 +135,8 @@ void main() {
'type': 'keydown', 'type': 'keydown',
'keymap': 'android', 'keymap': 'android',
'keyCode': 59, 'keyCode': 59,
'codePoint': null, 'plainCodePoint': 0,
'codePoint': 0,
'character': null, 'character': null,
'scanCode': 42, 'scanCode': 42,
'metaState': RawKeyEventDataAndroid.modifierLeftShift, '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