Unverified Commit 7959c395 authored by Tong Mu's avatar Tong Mu Committed by GitHub

[Keyboard] Correctly convert down events that are immediately synthesized released (#99200)

parent b44cbe1d
......@@ -887,6 +887,7 @@ class KeyEventManager {
final PhysicalKeyboardKey physicalKey = rawEvent.physicalKey;
final LogicalKeyboardKey logicalKey = rawEvent.logicalKey;
final Set<PhysicalKeyboardKey> physicalKeysPressed = _hardwareKeyboard.physicalKeysPressed;
final List<KeyEvent> eventAfterwards = <KeyEvent>[];
final KeyEvent? mainEvent;
final LogicalKeyboardKey? recordedLogicalMain = _hardwareKeyboard.lookUpLayout(physicalKey);
final Duration timeStamp = ServicesBinding.instance.currentSystemFrameTimeStamp;
......@@ -923,6 +924,17 @@ class KeyEventManager {
}
}
for (final PhysicalKeyboardKey key in physicalKeysPressed.difference(_rawKeyboard.physicalKeysPressed)) {
if (key == physicalKey) {
// Somehow, a down event is dispatched but the key is absent from
// keysPressed. Synthesize a up event for the key, but this event must
// be added after the main key down event.
eventAfterwards.add(KeyUpEvent(
physicalKey: key,
logicalKey: logicalKey,
timeStamp: timeStamp,
synthesized: true,
));
} else {
_keyEventsSinceLastMessage.add(KeyUpEvent(
physicalKey: key,
logicalKey: _hardwareKeyboard.lookUpLayout(key)!,
......@@ -930,6 +942,7 @@ class KeyEventManager {
synthesized: true,
));
}
}
for (final PhysicalKeyboardKey key in _rawKeyboard.physicalKeysPressed.difference(physicalKeysPressed)) {
_keyEventsSinceLastMessage.add(KeyDownEvent(
physicalKey: key,
......@@ -938,9 +951,11 @@ class KeyEventManager {
synthesized: true,
));
}
if (mainEvent != null)
if (mainEvent != null) {
_keyEventsSinceLastMessage.add(mainEvent);
}
_keyEventsSinceLastMessage.addAll(eventAfterwards);
}
/// Reset the inferred platform transit mode and related states.
///
......
......@@ -195,6 +195,56 @@ void main() {
logs.clear();
}, variant: KeySimulatorTransitModeVariant.all());
// Regression test for https://github.com/flutter/flutter/issues/99196 .
//
// In rawKeyData mode, if a key down event is dispatched but immediately
// synthesized to be released, the old logic would trigger a Null check
// _CastError on _hardwareKeyboard.lookUpLayout(key). The original scenario
// that this is triggered on Android is unknown. Here we make up a scenario
// where a ShiftLeft key down is dispatched but the modifier bit is not set.
testWidgets('Correctly convert down events that are synthesized released', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
final List<KeyEvent> events = <KeyEvent>[];
await tester.pumpWidget(
KeyboardListener(
autofocus: true,
focusNode: focusNode,
child: Container(),
onKeyEvent: (KeyEvent event) {
events.add(event);
},
),
);
// Dispatch an arbitrary event to bypass the pressedKeys check.
await simulateKeyDownEvent(LogicalKeyboardKey.keyA, platform: 'web');
// Dispatch an
final Map<String, dynamic> data2 = KeyEventSimulator.getKeyData(
LogicalKeyboardKey.shiftLeft,
platform: 'web',
)..['metaState'] = 0;
await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
SystemChannels.keyEvent.name,
SystemChannels.keyEvent.codec.encodeMessage(data2),
(ByteData? data) {},
);
expect(events, hasLength(3));
expect(events[1], isA<KeyDownEvent>());
expect(events[1].logicalKey, LogicalKeyboardKey.shiftLeft);
expect(events[1].synthesized, false);
expect(events[2], isA<KeyUpEvent>());
expect(events[2].logicalKey, LogicalKeyboardKey.shiftLeft);
expect(events[2].synthesized, true);
expect(ServicesBinding.instance.keyboard.physicalKeysPressed, equals(<PhysicalKeyboardKey>{
PhysicalKeyboardKey.keyA,
}));
}, variant: const KeySimulatorTransitModeVariant(<KeyDataTransitMode>{
KeyDataTransitMode.rawKeyData,
}));
testWidgets('Instantly dispatch synthesized key events when the queue is empty', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
final List<int> logs = <int>[];
......
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