Unverified Commit af050d95 authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Add a channel to query the engine keyboard state (#122885)

## Description

This PR adds a new channel to query the engine keyboard state.
See https://github.com/flutter/flutter/issues/87391#issuecomment-1228975571 for motivation. 

## Related Issue

Framework side implementation for https://github.com/flutter/flutter/issues/87391.

Once approved the framework will try to query the initial keyboard state from the engine. PRs will be needed on the engine side to answer the framework query.

## Tests

Adds 1 test.
parent 73a343f1
...@@ -68,8 +68,10 @@ mixin ServicesBinding on BindingBase, SchedulerBinding { ...@@ -68,8 +68,10 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
void _initKeyboard() { void _initKeyboard() {
_keyboard = HardwareKeyboard(); _keyboard = HardwareKeyboard();
_keyEventManager = KeyEventManager(_keyboard, RawKeyboard.instance); _keyEventManager = KeyEventManager(_keyboard, RawKeyboard.instance);
platformDispatcher.onKeyData = _keyEventManager.handleKeyData; _keyboard.syncKeyboardState().then((_) {
SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage); platformDispatcher.onKeyData = _keyEventManager.handleKeyData;
SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage);
});
} }
/// The default instance of [BinaryMessenger]. /// The default instance of [BinaryMessenger].
......
...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'binding.dart'; import 'binding.dart';
import 'raw_keyboard.dart'; import 'raw_keyboard.dart';
import 'system_channels.dart';
export 'dart:ui' show KeyData; export 'dart:ui' show KeyData;
...@@ -494,6 +495,20 @@ class HardwareKeyboard { ...@@ -494,6 +495,20 @@ class HardwareKeyboard {
} }
} }
/// Query the engine and update _pressedKeys accordingly to the engine answer.
Future<void> syncKeyboardState() async {
final Map<int, int>? keyboardState = await SystemChannels.keyboard.invokeMapMethod<int, int>(
'getKeyboardState',
);
if (keyboardState != null) {
for (final int key in keyboardState.keys) {
final PhysicalKeyboardKey physicalKey = PhysicalKeyboardKey(key);
final LogicalKeyboardKey logicalKey = LogicalKeyboardKey(keyboardState[key]!);
_pressedKeys[physicalKey] = logicalKey;
}
}
}
bool _dispatchKeyEvent(KeyEvent event) { bool _dispatchKeyEvent(KeyEvent event) {
// This dispatching could have used the same algorithm as [ChangeNotifier], // This dispatching could have used the same algorithm as [ChangeNotifier],
// but since 1) it shouldn't be necessary to support reentrantly // but since 1) it shouldn't be necessary to support reentrantly
......
...@@ -488,4 +488,22 @@ abstract final class SystemChannels { ...@@ -488,4 +488,22 @@ abstract final class SystemChannels {
'flutter/contextmenu', 'flutter/contextmenu',
JSONMethodCodec(), JSONMethodCodec(),
); );
/// A [MethodChannel] for retrieving keyboard pressed keys from the engine.
///
/// The following outgoing methods are defined for this channel (invoked using
/// [OptionalMethodChannel.invokeMethod]):
///
/// * `getKeyboardState`: Obtains keyboard pressed keys from the engine.
/// The keyboard state is sent as a `Map<int, int>?` where each entry
/// represents a pressed keyboard key. The entry key is the physical
/// key ID and the entry value is the logical key ID.
///
/// See also:
///
/// * [HardwareKeyboard.syncKeyboardState], which uses this channel to synchronize
/// the `HardwareKeyboard` pressed state.
static const MethodChannel keyboard = OptionalMethodChannel(
'flutter/keyboard',
);
} }
...@@ -46,7 +46,13 @@ class TestBinding extends BindingBase with SchedulerBinding, ServicesBinding { ...@@ -46,7 +46,13 @@ class TestBinding extends BindingBase with SchedulerBinding, ServicesBinding {
@override @override
TestDefaultBinaryMessenger createBinaryMessenger() { TestDefaultBinaryMessenger createBinaryMessenger() {
return TestDefaultBinaryMessenger(super.createBinaryMessenger()); Future<ByteData?> keyboardHandler(ByteData? message) async {
return const StandardMethodCodec().encodeSuccessEnvelope(<int, int>{1:1});
}
return TestDefaultBinaryMessenger(
super.createBinaryMessenger(),
outboundHandlers: <String, MessageHandler>{'flutter/keyboard': keyboardHandler},
);
} }
} }
...@@ -145,4 +151,14 @@ void main() { ...@@ -145,4 +151,14 @@ void main() {
expect(result, isNotNull); expect(result, isNotNull);
expect(result!['response'], equals('exit')); expect(result!['response'], equals('exit'));
}); });
test('initInstances synchronizes keyboard state', () async {
final Set<PhysicalKeyboardKey> physicalKeys = HardwareKeyboard.instance.physicalKeysPressed;
final Set<LogicalKeyboardKey> logicalKeys = HardwareKeyboard.instance.logicalKeysPressed;
expect(physicalKeys.length, 1);
expect(logicalKeys.length, 1);
expect(physicalKeys.first, const PhysicalKeyboardKey(1));
expect(logicalKeys.first, const LogicalKeyboardKey(1));
});
} }
...@@ -107,7 +107,13 @@ mixin TestDefaultBinaryMessengerBinding on BindingBase, ServicesBinding { ...@@ -107,7 +107,13 @@ mixin TestDefaultBinaryMessengerBinding on BindingBase, ServicesBinding {
@override @override
TestDefaultBinaryMessenger createBinaryMessenger() { TestDefaultBinaryMessenger createBinaryMessenger() {
return TestDefaultBinaryMessenger(super.createBinaryMessenger()); Future<ByteData?> keyboardHandler(ByteData? message) async {
return const StandardMethodCodec().encodeSuccessEnvelope(<int, int>{});
}
return TestDefaultBinaryMessenger(
super.createBinaryMessenger(),
outboundHandlers: <String, MessageHandler>{'flutter/keyboard': keyboardHandler},
);
} }
} }
......
...@@ -49,7 +49,12 @@ class TestDefaultBinaryMessenger extends BinaryMessenger { ...@@ -49,7 +49,12 @@ class TestDefaultBinaryMessenger extends BinaryMessenger {
/// Creates a [TestDefaultBinaryMessenger] instance. /// Creates a [TestDefaultBinaryMessenger] instance.
/// ///
/// The [delegate] instance must not be null. /// The [delegate] instance must not be null.
TestDefaultBinaryMessenger(this.delegate); TestDefaultBinaryMessenger(
this.delegate, {
Map<String, MessageHandler> outboundHandlers = const <String, MessageHandler>{},
}) {
_outboundHandlers.addAll(outboundHandlers);
}
/// The delegate [BinaryMessenger]. /// The delegate [BinaryMessenger].
final BinaryMessenger delegate; final BinaryMessenger delegate;
......
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