From c5573ea3d8063c9f7b57be6bf4097a7df9bfa33d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 29 Jul 2021 19:19:04 -0700 Subject: [PATCH] [Keyboard] Accept empty events (#87152) --- .../lib/src/services/hardware_keyboard.dart | 9 ++- .../test/services/hardware_keyboard_test.dart | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/services/hardware_keyboard.dart b/packages/flutter/lib/src/services/hardware_keyboard.dart index dc579922d9..03130f70df 100644 --- a/packages/flutter/lib/src/services/hardware_keyboard.dart +++ b/packages/flutter/lib/src/services/hardware_keyboard.dart @@ -787,8 +787,15 @@ class KeyEventManager { assert(false, 'Should never encounter KeyData when transitMode is rawKeyData.'); return false; case KeyDataTransitMode.keyDataThenRawKeyData: + assert((data.physical == 0 && data.logical == 0) || + (data.physical != 0 && data.logical != 0)); // Postpone key event dispatching until the handleRawKeyMessage. - _keyEventsSinceLastMessage.add(_eventFromData(data)); + // + // Having 0 as the physical or logical ID indicates an empty key data, + // transmitted to ensure that the transit mode is correctly inferred. + if (data.physical != 0 && data.logical != 0) { + _keyEventsSinceLastMessage.add(_eventFromData(data)); + } return false; } } diff --git a/packages/flutter/test/services/hardware_keyboard_test.dart b/packages/flutter/test/services/hardware_keyboard_test.dart index 634a2891de..7b41927489 100644 --- a/packages/flutter/test/services/hardware_keyboard_test.dart +++ b/packages/flutter/test/services/hardware_keyboard_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui' as ui; + import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -162,4 +164,60 @@ void main() { expect(logs, [3, 2, 1]); logs.clear(); }, variant: KeySimulatorTransitModeVariant.all()); + + // The first key data received from the engine might be an empty key data. + // In that case, the key data should not be converted to any [KeyEvent]s, + // but is only used so that *a* key data comes before the raw key message + // and makes [KeyEventManager] infer [KeyDataTransitMode.keyDataThenRawKeyData]. + testWidgets('Empty keyData yields no event but triggers inferrence', (WidgetTester tester) async { + final List events = []; + final List rawEvents = []; + tester.binding.keyboard.addHandler((KeyEvent event) { + events.add(event); + return true; + }); + RawKeyboard.instance.addListener((RawKeyEvent event) { + rawEvents.add(event); + }); + tester.binding.keyEventManager.handleKeyData(const ui.KeyData( + type: ui.KeyEventType.down, + timeStamp: Duration.zero, + logical: 0, + physical: 0, + character: 'a', + synthesized: false, + )); + tester.binding.keyEventManager.handleRawKeyMessage({ + 'type': 'keydown', + 'keymap': 'windows', + 'keyCode': 0x04, + 'scanCode': 0x04, + 'characterCodePoint': 0, + 'modifiers': 0, + }); + expect(events.length, 0); + expect(rawEvents.length, 1); + + // Dispatch another key data to ensure it's in + // [KeyDataTransitMode.keyDataThenRawKeyData] mode (otherwise assertion + // will be thrown upon a KeyData). + tester.binding.keyEventManager.handleKeyData(const ui.KeyData( + type: ui.KeyEventType.down, + timeStamp: Duration.zero, + logical: 0x22, + physical: 0x70034, + character: '"', + synthesized: false, + )); + tester.binding.keyEventManager.handleRawKeyMessage({ + 'type': 'keydown', + 'keymap': 'windows', + 'keyCode': 0x04, + 'scanCode': 0x04, + 'characterCodePoint': 0, + 'modifiers': 0, + }); + expect(events.length, 1); + expect(rawEvents.length, 2); + }); }