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 {
void _initKeyboard() {
_keyboard = HardwareKeyboard();
_keyEventManager = KeyEventManager(_keyboard, RawKeyboard.instance);
_keyboard.syncKeyboardState().then((_) {
platformDispatcher.onKeyData = _keyEventManager.handleKeyData;
SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage);
});
}
/// The default instance of [BinaryMessenger].
......
......@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'binding.dart';
import 'raw_keyboard.dart';
import 'system_channels.dart';
export 'dart:ui' show KeyData;
......@@ -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) {
// This dispatching could have used the same algorithm as [ChangeNotifier],
// but since 1) it shouldn't be necessary to support reentrantly
......
......@@ -488,4 +488,22 @@ abstract final class SystemChannels {
'flutter/contextmenu',
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 {
@override
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() {
expect(result, isNotNull);
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 {
@override
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 {
/// Creates a [TestDefaultBinaryMessenger] instance.
///
/// 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].
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