diff --git a/dev/manual_tests/raw_keyboard.dart b/dev/manual_tests/raw_keyboard.dart index dc54956283..1d7dfddb7a 100644 --- a/dev/manual_tests/raw_keyboard.dart +++ b/dev/manual_tests/raw_keyboard.dart @@ -3,8 +3,7 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; - -import 'package:flutter_services/input_event.dart' as mojom; +import 'package:flutter/services.dart'; GlobalKey _key = new GlobalKey(); @@ -32,9 +31,9 @@ class RawKeyboardDemo extends StatefulWidget { } class _HardwareKeyDemoState extends State { - mojom.InputEvent _event; + RawKeyEvent _event; - void _handleKey(mojom.InputEvent event) { + void _handleKeyEvent(RawKeyEvent event) { setState(() { _event = event; }); @@ -50,26 +49,34 @@ class _HardwareKeyDemoState extends State { Focus.moveTo(config.key); }, child: new Center( - child: new Text('Tap to focus', style: Typography.black.display1) - ) + child: new Text('Tap to focus', style: Typography.black.display1), + ), ); } else if (_event == null) { child = new Center( - child: new Text('Press a key', style: Typography.black.display1) + child: new Text('Press a key', style: Typography.black.display1), ); } else { + int codePoint; + int keyCode; + final RawKeyEventData data = _event.data; + if (data is RawKeyEventDataAndroid) { + codePoint = data.codePoint; + keyCode = data.keyCode; + } child = new Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - new Text('${_event.type}', style: Typography.black.body2), - new Text('${_event.keyData.keyCode}', style: Typography.black.display4) + new Text('${_event.runtimeType}', style: Typography.black.body2), + new Text('codePoint: $codePoint', style: Typography.black.display4), + new Text('keyCode: $keyCode', style: Typography.black.display4), ], - mainAxisAlignment: MainAxisAlignment.center ); } return new RawKeyboardListener( focused: focused, - onKey: _handleKey, - child: child + onKey: _handleKeyEvent, + child: child, ); } } diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index 31bdc04054..bf3b3baa97 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -27,6 +27,7 @@ export 'src/services/image_stream.dart'; export 'src/services/keyboard.dart'; export 'src/services/path_provider.dart'; export 'src/services/platform_messages.dart'; +export 'src/services/raw_keyboard.dart'; export 'src/services/shell.dart'; export 'src/services/system_chrome.dart'; export 'src/services/system_navigator.dart'; diff --git a/packages/flutter/lib/src/services/raw_keyboard.dart b/packages/flutter/lib/src/services/raw_keyboard.dart new file mode 100644 index 0000000000..a38f415d64 --- /dev/null +++ b/packages/flutter/lib/src/services/raw_keyboard.dart @@ -0,0 +1,176 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; + +import 'platform_messages.dart'; + +/// Base class for platform specific key event data. +/// +/// This base class exists to have a common type to use for each of the +/// target platform's key event data structures. +/// +/// See also: +/// +/// * [RawKeyEventDataAndroid] +/// * [RawKeyEvent] +/// * [RawKeyDownEvent] +/// * [RawKeyUpEvent] +abstract class RawKeyEventData { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const RawKeyEventData(); +} + +/// Platform-specific key event data for Android. +/// +/// This object contains information about key events obtained from Android's +/// KeyEvent interface. +class RawKeyEventDataAndroid extends RawKeyEventData { + /// Creates a key event data structure specific for Android. + /// + /// The [flags], [codePoint], [keyCode], [scanCode], and [metaState] arguments + /// must not be null. + const RawKeyEventDataAndroid({ + this.flags: 0, + this.codePoint: 0, + this.keyCode: 0, + this.scanCode: 0, + this.metaState: 0, + }); + + /// See + final int flags; + + /// See + final int codePoint; + + /// See + final int keyCode; + + /// See + final int scanCode; + + /// See + final int metaState; +} + +/// Base class for raw key events. +/// +/// Raw key events pass through as much information as possible from the +/// underlying platform's key events, which makes they provide a high level of +/// fidelity but a low level of portability. +/// +/// See also: +/// +/// * [RawKeyDownEvent] +/// * [RawKeyUpEvent] +/// * [RawKeyboardListener], a widget that listens for raw key events. +abstract class RawKeyEvent { + /// Initializes fields for subclasses. + const RawKeyEvent({ + @required this.data, + }); + + /// Platform-specific information about the key event. + final RawKeyEventData data; +} + +/// The user has pressed a key on the keyboard. +class RawKeyDownEvent extends RawKeyEvent { + /// Creates a key event that represents the user pressing a key. + const RawKeyDownEvent({ + @required RawKeyEventData data, + }) : super(data: data); +} + +/// The user has released a key on the keyboard. +class RawKeyUpEvent extends RawKeyEvent { + /// Creates a key event that represents the user releasing a key. + const RawKeyUpEvent({ + @required RawKeyEventData data, + }) : super(data: data); +} + +RawKeyEvent _toRawKeyEvent(dynamic message) { + RawKeyEventData data; + + String keymap = message['keymap']; + switch (keymap) { + case 'android': + data = new RawKeyEventDataAndroid( + flags: message['flags'] ?? 0, + codePoint: message['codePoint'] ?? 0, + keyCode: message['keyCode'] ?? 0, + scanCode: message['scanCode'] ?? 0, + metaState: message['metaState'] ?? 0, + ); + break; + default: + throw new FlutterError('Unknown keymap for key events: $keymap'); + } + + String type = message['type']; + switch (type) { + case 'keydown': + return new RawKeyDownEvent(data: data); + case 'keyup': + return new RawKeyUpEvent(data: data); + } + throw new FlutterError('Unknown key event type: $type'); +} + +/// An interface for listening to raw key events. +/// +/// Raw key events pass through as much information as possible from the +/// underlying platform's key events, which makes they provide a high level of +/// fidelity but a low level of portability. +/// +/// A [RawKeyboard] is useful for listening to raw key events and hardware +/// buttons that are represented as keys. Typically used by games and other apps +/// that use keyboards for purposes other than text entry. +/// +/// See also: +/// +/// * [RawKeyEvent] +/// * [RawKeyDownEvent] +/// * [RawKeyUpEvent] +class RawKeyboard { + RawKeyboard._() { + PlatformMessages.setJSONMessageHandler('flutter/keyevent', _handleKeyEvent); + } + + /// The shared instance of [RawKeyboard]. + static final RawKeyboard instance = new RawKeyboard._(); + + final List> _listeners = >[]; + + /// Calls the listener every time the user presses or releases a key. + /// + /// Listeners can be removed with [removeListener]. + void addListener(ValueChanged listener) { + _listeners.add(listener); + } + + /// Stop calling the listener every time the user presses or releases a key. + /// + /// Listeners can be added with [addListener]. + void removeListener(ValueChanged listener) { + _listeners.remove(listener); + } + + Future _handleKeyEvent(dynamic message) async { + if (_listeners.isEmpty) + return; + RawKeyEvent event = _toRawKeyEvent(message); + if (event == null) + return; + for (ValueChanged listener in new List>.from(_listeners)) + if (_listeners.contains(listener)) + listener(event); + } +} diff --git a/packages/flutter/lib/src/widgets/raw_keyboard_listener.dart b/packages/flutter/lib/src/widgets/raw_keyboard_listener.dart index fdcd7c1a26..c0220c0ac1 100644 --- a/packages/flutter/lib/src/widgets/raw_keyboard_listener.dart +++ b/packages/flutter/lib/src/widgets/raw_keyboard_listener.dart @@ -3,13 +3,12 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:flutter_services/raw_keyboard.dart' as mojom; -import 'package:flutter_services/input_event.dart' as mojom; import 'basic.dart'; import 'framework.dart'; -/// A widget that calls a callback whenever the user presses a key on a keyboard. +/// A widget that calls a callback whenever the user presses or releases a key +/// on a keyboard. /// /// A [RawKeyboardListener] is useful for listening to raw key events and /// hardware buttons that are represented as keys. Typically used by games and @@ -43,7 +42,7 @@ class RawKeyboardListener extends StatefulWidget { final bool focused; /// Called whenever this widget receives a raw keyboard event. - final ValueChanged onKey; + final ValueChanged onKey; /// The widget below this widget in the tree. final Widget child; @@ -52,15 +51,13 @@ class RawKeyboardListener extends StatefulWidget { _RawKeyboardListenerState createState() => new _RawKeyboardListenerState(); } -class _RawKeyboardListenerState extends State implements mojom.RawKeyboardListener { +class _RawKeyboardListenerState extends State { @override void initState() { super.initState(); _attachOrDetachKeyboard(); } - mojom.RawKeyboardListenerStub _stub; - @override void didUpdateConfig(RawKeyboardListener oldConfig) { _attachOrDetachKeyboard(); @@ -79,22 +76,21 @@ class _RawKeyboardListenerState extends State implements mo _detachKeyboardIfAttached(); } + bool _listening = false; + void _attachKeyboardIfDetached() { - if (_stub != null) + if (_listening) return; - _stub = new mojom.RawKeyboardListenerStub.unbound()..impl = this; - mojom.RawKeyboardServiceProxy keyboard = shell.connectToViewAssociatedService(mojom.RawKeyboardService.connectToService); - keyboard.addListener(_stub); - keyboard.close(); + RawKeyboard.instance.addListener(_handleRawKeyEvent); } void _detachKeyboardIfAttached() { - _stub?.close(); - _stub = null; + if (!_listening) + return; + RawKeyboard.instance.removeListener(_handleRawKeyEvent); } - @override - void onKey(mojom.InputEvent event) { + void _handleRawKeyEvent(RawKeyEvent event) { if (config.onKey != null) config.onKey(event); }