From 02258ddb986e5e8efd2626b1d9e0dd64e4ad5a9b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 6 Jan 2021 15:03:30 -0800 Subject: [PATCH] Split android out --- shell/platform/android/BUILD.gn | 2 + .../android/AndroidTouchProcessor.java | 2 +- .../embedding/android/FlutterView.java | 19 +- .../embedding/android/HardwareKeyboard.java | 196 +++++++ .../embedding/android/KeyboardMap.java | 510 ++++++++++++++++++ .../flutter/embedding/engine/FlutterJNI.java | 13 + .../engine/renderer/FlutterRenderer.java | 5 + .../android/io/flutter/view/FlutterView.java | 20 +- .../android/keycodes/keyboard_map_android.h | 459 ++++++++++++++++ .../android/platform_view_android_jni_impl.cc | 16 + .../android/HardwareKeyboardTest.java | 57 ++ tools/android_lint/project.xml | 2 + 12 files changed, 1298 insertions(+), 3 deletions(-) create mode 100644 shell/platform/android/io/flutter/embedding/android/HardwareKeyboard.java create mode 100644 shell/platform/android/io/flutter/embedding/android/KeyboardMap.java create mode 100644 shell/platform/android/keycodes/keyboard_map_android.h create mode 100644 shell/platform/android/test/io/flutter/embedding/android/HardwareKeyboardTest.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 9aa06b754774b..f550d35428874 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -143,6 +143,8 @@ android_java_sources = [ "io/flutter/embedding/android/FlutterSurfaceView.java", "io/flutter/embedding/android/FlutterTextureView.java", "io/flutter/embedding/android/FlutterView.java", + "io/flutter/embedding/android/HardwareKeyboard.java", + "io/flutter/embedding/android/KeyboardMap.java", "io/flutter/embedding/android/MotionEventTracker.java", "io/flutter/embedding/android/RenderMode.java", "io/flutter/embedding/android/SplashScreen.java", diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java index 7d324c25700af..71854dd1557f1 100644 --- a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java +++ b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java @@ -57,7 +57,7 @@ public class AndroidTouchProcessor { int UNKNOWN = 2; } - // Must match the unpacking code in hooks.dart. + // Must match _kPointerDataFieldCount in platform_dispatcher.dart. private static final int POINTER_DATA_FIELD_COUNT = 29; private static final int BYTES_PER_FIELD = 8; diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 2b886bc9c2475..92c576f523635 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -34,6 +34,7 @@ import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import io.flutter.Log; +import io.flutter.embedding.android.HardwareKeyboard; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener; @@ -44,8 +45,10 @@ import io.flutter.plugin.mouse.MouseCursorPlugin; import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.view.AccessibilityBridge; +import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; +import java.util.List; /** * Displays a Flutter UI on an Android device. @@ -104,6 +107,7 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC @Nullable private AndroidKeyProcessor androidKeyProcessor; @Nullable private AndroidTouchProcessor androidTouchProcessor; @Nullable private AccessibilityBridge accessibilityBridge; + @Nullable private HardwareKeyboard hardwareKeyboard; // Directly implemented View behavior that communicates with Flutter. private final FlutterRenderer.ViewportMetrics viewportMetrics = @@ -720,6 +724,18 @@ public boolean checkInputConnectionProxy(View view) { : super.checkInputConnectionProxy(view); } + private boolean onHardwareKeyEvent(@NonNull KeyEvent event) { + final List keyData = hardwareKeyboard.convertEvent(event); + if (keyData != null) { + final FlutterRenderer renderer = flutterEngine.getRenderer(); + for (HardwareKeyboard.KeyDatum keyDatum : keyData) { + final ByteBuffer packet = hardwareKeyboard.packDatum(keyDatum); + renderer.dispatchKeyDataPacket(packet, packet.position()); + } + } + return true; + } + /** * Invoked when a hardware key is pressed or released. * @@ -744,7 +760,7 @@ public boolean dispatchKeyEvent(KeyEvent event) { // superclass. The key processor will typically handle all events except // those where it has re-dispatched the event after receiving a reply from // the framework that the framework did not handle it. - return (isAttachedToFlutterEngine() && androidKeyProcessor.onKeyEvent(event)) + return (isAttachedToFlutterEngine() && (onHardwareKeyEvent(event) || androidKeyProcessor.onKeyEvent(event))) || super.dispatchKeyEvent(event); } @@ -896,6 +912,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { localizationPlugin = this.flutterEngine.getLocalizationPlugin(); androidKeyProcessor = new AndroidKeyProcessor(this, this.flutterEngine.getKeyEventChannel(), textInputPlugin); + hardwareKeyboard = new HardwareKeyboard(); androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer(), /*trackMotionEvents=*/ false); accessibilityBridge = diff --git a/shell/platform/android/io/flutter/embedding/android/HardwareKeyboard.java b/shell/platform/android/io/flutter/embedding/android/HardwareKeyboard.java new file mode 100644 index 0000000000000..d6b3abb1f4c1b --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/HardwareKeyboard.java @@ -0,0 +1,196 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.view.KeyEvent; +import android.view.KeyCharacterMap; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.embedding.android.KeyboardMap; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +public class HardwareKeyboard { + // Must match the KeyChange enum in key.dart. + @IntDef({ + KeyChange.DOWN, + KeyChange.UP, + KeyChange.REPEAT, + }) + private @interface KeyChange { + int DOWN = 0; + int UP = 1; + int REPEAT = 2; + } + + // Must match _kKeyDataFieldCount in platform_dispatcher.dart. + private static final int KEY_DATA_FIELD_COUNT = 5; + private static final int BYTES_PER_FIELD = 8; + + private final HashMap mPressingRecords = new HashMap(); + + public HardwareKeyboard() { + } + + static long logicalKeyFromEvent(KeyEvent event) { + // `KeyEvent#getDisplayLabel` may be another source of "key without + // modifier", but tests so far have shown that they yield the same result + // as from `KeyEvent#getKeyCode. + final int keyCode = event.getKeyCode(); + final Long mapResult = KeyboardMap.keyCodeToLogical.get(Long.valueOf(keyCode)); + if (mapResult != null) + return mapResult.longValue(); + return keyCode; + } + + static long physicalKeyFromEvent(KeyEvent event) { + final int scanCode = event.getScanCode(); + final Long mapResult = KeyboardMap.scanCodeToPhysical.get(Long.valueOf(scanCode)); + if (mapResult != null) + return mapResult.longValue(); + return scanCode; + } + + private long getLastLogicalRecord(long physicalKey) { + final Long objValue = mPressingRecords.get(Long.valueOf(physicalKey)); + if (objValue == null) { + return 0; + } + return objValue.longValue(); + } + + // May return null + public List convertEvent(KeyEvent event) { + final long logicalKey = logicalKeyFromEvent(event); + final long physicalKey = physicalKeyFromEvent(event); + final boolean isPhysicalDown = event.getAction() == KeyEvent.ACTION_DOWN; + final long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. + + final long lastLogicalRecord = getLastLogicalRecord(physicalKey); + + final int deviceId = event.getDeviceId(); + final KeyCharacterMap kcm = KeyCharacterMap.load(deviceId); + final int keyCode = event.getKeyCode(); + final char ch = kcm.getDisplayLabel(keyCode); + + int change; + + if (isPhysicalDown) { + if (lastLogicalRecord != 0) { + // This physical key is being pressed according to the record. + if (event.getRepeatCount() == 0) { + // A non-repeated key has been pressed that has the exact physical key as + // a currently pressed one, usually indicating multiple keyboards are + // pressing keys with the same physical key, or the up event was lost + // during a loss of focus. The down event is ignored. + return null; + } else { + // A normal repeated key. + change = KeyChange.REPEAT; + } + } else { + // This physical key is not being pressed according to the record. It's a + // normal down event, whether the system event is a repeat or not. + change = KeyChange.DOWN; + } + } else { // isPhysicalDown false + if (lastLogicalRecord == 0) { + // The physical key has been released before. It indicates multiple + // keyboards pressed keys with the same physical key. Ignore the up event. + return null; + } + + change = KeyChange.UP; + } + + Long nextLogicalRecord = null; + switch (change) { + case KeyChange.DOWN: + nextLogicalRecord = logicalKey; + break; + case KeyChange.UP: + nextLogicalRecord = null; + break; + case KeyChange.REPEAT: + nextLogicalRecord = lastLogicalRecord; + break; + } + if (nextLogicalRecord == null) { + mPressingRecords.remove(physicalKey); + } else { + mPressingRecords.put(physicalKey, nextLogicalRecord); + } + + final int characterChar = event.getUnicodeChar(); + final String characterStr = characterChar == 0 || (characterChar & KeyCharacterMap.COMBINING_ACCENT) != 0 ? + new String() : new String(new char[]{(char)characterChar}); + final KeyDatum keyDatum = new KeyDatum( + change, + timeStamp, + physicalKey, + logicalKey, + characterStr, + false); + + final List keyData = new ArrayList(); + keyData.add(keyDatum); + return keyData; + } + + public ByteBuffer packDatum(KeyDatum keyDatum) { + System.out.printf("character %s\n", keyDatum.character.length() == 0 ? "N/A" : keyDatum.character); + final byte[] charBytes = keyDatum.character.length() == 0 ? new byte[0] + : keyDatum.character.getBytes(StandardCharsets.UTF_8); + // Structure of [packet]: + // + // * charBytes.length (1 field) + // * keyDatum (KEY_DATA_FIELD_COUNT fields) + // * characters (charBytes.length bytes) + final ByteBuffer packet = + ByteBuffer.allocateDirect((1 + KEY_DATA_FIELD_COUNT) * BYTES_PER_FIELD + charBytes.length); + packet.order(ByteOrder.LITTLE_ENDIAN); + + packet.putLong(charBytes.length); + + packet.putLong(keyDatum.timeStamp); + packet.putLong(keyDatum.change); + packet.putLong(keyDatum.physical); + packet.putLong(keyDatum.logical); + final long synthesized = keyDatum.synthesized ? 1 : 0; + packet.putLong(synthesized); + packet.put(ByteBuffer.wrap(charBytes)); + return packet; + } + + public static class KeyDatum { + @NonNull public final int change; + // Time in microseconds from an arbitrary and consistent start. + @NonNull public final long timeStamp; + @NonNull public final long physical; + @NonNull public final long logical; + @Nullable public final String character; + @NonNull public final boolean synthesized; + + public KeyDatum( + @NonNull int change, + @NonNull long timeStamp, + @NonNull long physical, + @NonNull long logical, + @Nullable String character, + @NonNull boolean synthesized) { + this.change = change; + this.timeStamp = timeStamp; + this.physical = physical; + this.logical = logical; + this.character = character; + this.synthesized = synthesized; + } + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/KeyboardMap.java b/shell/platform/android/io/flutter/embedding/android/KeyboardMap.java new file mode 100644 index 0000000000000..afccf37662ce0 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/KeyboardMap.java @@ -0,0 +1,510 @@ +package io.flutter.embedding.android; + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/android_keyboard_map_java.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +import java.util.HashMap; + +public class KeyboardMap { + public static final HashMap scanCodeToPhysical = new HashMap() { + private static final long serialVersionUID = 1L; + { + put(0x00000000cdL, 0x0000000014L); // suspend + put(0x000000008eL, 0x0000010082L); // sleep + put(0x000000008fL, 0x0000010083L); // wakeUp + put(0x000000001eL, 0x0000070004L); // keyA + put(0x0000000030L, 0x0000070005L); // keyB + put(0x000000002eL, 0x0000070006L); // keyC + put(0x0000000020L, 0x0000070007L); // keyD + put(0x0000000012L, 0x0000070008L); // keyE + put(0x0000000021L, 0x0000070009L); // keyF + put(0x0000000022L, 0x000007000aL); // keyG + put(0x0000000023L, 0x000007000bL); // keyH + put(0x0000000017L, 0x000007000cL); // keyI + put(0x0000000024L, 0x000007000dL); // keyJ + put(0x0000000025L, 0x000007000eL); // keyK + put(0x0000000026L, 0x000007000fL); // keyL + put(0x0000000032L, 0x0000070010L); // keyM + put(0x0000000031L, 0x0000070011L); // keyN + put(0x0000000018L, 0x0000070012L); // keyO + put(0x0000000019L, 0x0000070013L); // keyP + put(0x0000000010L, 0x0000070014L); // keyQ + put(0x0000000013L, 0x0000070015L); // keyR + put(0x000000001fL, 0x0000070016L); // keyS + put(0x0000000014L, 0x0000070017L); // keyT + put(0x0000000016L, 0x0000070018L); // keyU + put(0x000000002fL, 0x0000070019L); // keyV + put(0x0000000011L, 0x000007001aL); // keyW + put(0x000000002dL, 0x000007001bL); // keyX + put(0x0000000015L, 0x000007001cL); // keyY + put(0x000000002cL, 0x000007001dL); // keyZ + put(0x0000000002L, 0x000007001eL); // digit1 + put(0x0000000003L, 0x000007001fL); // digit2 + put(0x0000000004L, 0x0000070020L); // digit3 + put(0x0000000005L, 0x0000070021L); // digit4 + put(0x0000000006L, 0x0000070022L); // digit5 + put(0x0000000007L, 0x0000070023L); // digit6 + put(0x0000000008L, 0x0000070024L); // digit7 + put(0x0000000009L, 0x0000070025L); // digit8 + put(0x000000000aL, 0x0000070026L); // digit9 + put(0x000000000bL, 0x0000070027L); // digit0 + put(0x000000001cL, 0x0000070028L); // enter + put(0x0000000001L, 0x0000070029L); // escape + put(0x000000000eL, 0x000007002aL); // backspace + put(0x000000000fL, 0x000007002bL); // tab + put(0x0000000039L, 0x000007002cL); // space + put(0x000000000cL, 0x000007002dL); // minus + put(0x000000000dL, 0x000007002eL); // equal + put(0x000000001aL, 0x000007002fL); // bracketLeft + put(0x000000001bL, 0x0000070030L); // bracketRight + put(0x000000002bL, 0x0000070031L); // backslash + put(0x0000000056L, 0x0000070031L); // backslash + put(0x0000000027L, 0x0000070033L); // semicolon + put(0x0000000028L, 0x0000070034L); // quote + put(0x0000000029L, 0x0000070035L); // backquote + put(0x0000000033L, 0x0000070036L); // comma + put(0x0000000034L, 0x0000070037L); // period + put(0x0000000035L, 0x0000070038L); // slash + put(0x000000003aL, 0x0000070039L); // capsLock + put(0x000000003bL, 0x000007003aL); // f1 + put(0x000000003cL, 0x000007003bL); // f2 + put(0x000000003dL, 0x000007003cL); // f3 + put(0x000000003eL, 0x000007003dL); // f4 + put(0x000000003fL, 0x000007003eL); // f5 + put(0x0000000040L, 0x000007003fL); // f6 + put(0x0000000041L, 0x0000070040L); // f7 + put(0x0000000042L, 0x0000070041L); // f8 + put(0x0000000043L, 0x0000070042L); // f9 + put(0x0000000044L, 0x0000070043L); // f10 + put(0x0000000057L, 0x0000070044L); // f11 + put(0x0000000058L, 0x0000070045L); // f12 + put(0x0000000063L, 0x0000070046L); // printScreen + put(0x0000000046L, 0x0000070047L); // scrollLock + put(0x0000000077L, 0x0000070048L); // pause + put(0x000000019bL, 0x0000070048L); // pause + put(0x000000006eL, 0x0000070049L); // insert + put(0x0000000066L, 0x000007004aL); // home + put(0x0000000068L, 0x000007004bL); // pageUp + put(0x00000000b1L, 0x000007004bL); // pageUp + put(0x000000006fL, 0x000007004cL); // delete + put(0x000000006bL, 0x000007004dL); // end + put(0x000000006dL, 0x000007004eL); // pageDown + put(0x00000000b2L, 0x000007004eL); // pageDown + put(0x000000006aL, 0x000007004fL); // arrowRight + put(0x0000000069L, 0x0000070050L); // arrowLeft + put(0x000000006cL, 0x0000070051L); // arrowDown + put(0x0000000067L, 0x0000070052L); // arrowUp + put(0x0000000045L, 0x0000070053L); // numLock + put(0x0000000062L, 0x0000070054L); // numpadDivide + put(0x0000000037L, 0x0000070055L); // numpadMultiply + put(0x000000004aL, 0x0000070056L); // numpadSubtract + put(0x000000004eL, 0x0000070057L); // numpadAdd + put(0x0000000060L, 0x0000070058L); // numpadEnter + put(0x000000004fL, 0x0000070059L); // numpad1 + put(0x0000000050L, 0x000007005aL); // numpad2 + put(0x0000000051L, 0x000007005bL); // numpad3 + put(0x000000004bL, 0x000007005cL); // numpad4 + put(0x000000004cL, 0x000007005dL); // numpad5 + put(0x000000004dL, 0x000007005eL); // numpad6 + put(0x0000000047L, 0x000007005fL); // numpad7 + put(0x0000000048L, 0x0000070060L); // numpad8 + put(0x0000000049L, 0x0000070061L); // numpad9 + put(0x0000000052L, 0x0000070062L); // numpad0 + put(0x0000000053L, 0x0000070063L); // numpadDecimal + put(0x000000007fL, 0x0000070065L); // contextMenu + put(0x000000008bL, 0x0000070065L); // contextMenu + put(0x0000000074L, 0x0000070066L); // power + put(0x0000000098L, 0x0000070066L); // power + put(0x0000000075L, 0x0000070067L); // numpadEqual + put(0x00000000b7L, 0x0000070068L); // f13 + put(0x00000000b8L, 0x0000070069L); // f14 + put(0x00000000b9L, 0x000007006aL); // f15 + put(0x00000000baL, 0x000007006bL); // f16 + put(0x00000000bbL, 0x000007006cL); // f17 + put(0x00000000bcL, 0x000007006dL); // f18 + put(0x00000000bdL, 0x000007006eL); // f19 + put(0x00000000beL, 0x000007006fL); // f20 + put(0x00000000bfL, 0x0000070070L); // f21 + put(0x00000000c0L, 0x0000070071L); // f22 + put(0x00000000c1L, 0x0000070072L); // f23 + put(0x00000000c2L, 0x0000070073L); // f24 + put(0x0000000086L, 0x0000070074L); // open + put(0x000000008aL, 0x0000070075L); // help + put(0x0000000161L, 0x0000070077L); // select + put(0x0000000081L, 0x0000070079L); // again + put(0x0000000083L, 0x000007007aL); // undo + put(0x0000000089L, 0x000007007bL); // cut + put(0x0000000085L, 0x000007007cL); // copy + put(0x0000000087L, 0x000007007dL); // paste + put(0x0000000088L, 0x000007007eL); // find + put(0x0000000071L, 0x000007007fL); // audioVolumeMute + put(0x0000000073L, 0x0000070080L); // audioVolumeUp + put(0x0000000072L, 0x0000070081L); // audioVolumeDown + put(0x000000005fL, 0x0000070085L); // numpadComma + put(0x0000000079L, 0x0000070085L); // numpadComma + put(0x0000000059L, 0x0000070087L); // intlRo + put(0x000000007cL, 0x0000070089L); // intlYen + put(0x000000005cL, 0x000007008aL); // convert + put(0x000000005eL, 0x000007008bL); // nonConvert + put(0x000000005aL, 0x0000070092L); // lang3 + put(0x000000005bL, 0x0000070093L); // lang4 + put(0x0000000082L, 0x00000700a3L); // props + put(0x00000000b3L, 0x00000700b6L); // numpadParenLeft + put(0x00000000b4L, 0x00000700b7L); // numpadParenRight + put(0x000000001dL, 0x00000700e0L); // controlLeft + put(0x000000002aL, 0x00000700e1L); // shiftLeft + put(0x0000000038L, 0x00000700e2L); // altLeft + put(0x000000007dL, 0x00000700e3L); // metaLeft + put(0x0000000061L, 0x00000700e4L); // controlRight + put(0x0000000036L, 0x00000700e5L); // shiftRight + put(0x0000000064L, 0x00000700e6L); // altRight + put(0x000000007eL, 0x00000700e7L); // metaRight + put(0x0000000166L, 0x00000c0060L); // info + put(0x0000000172L, 0x00000c0061L); // closedCaptionToggle + put(0x00000000e1L, 0x00000c006fL); // brightnessUp + put(0x00000000e0L, 0x00000c0070L); // brightnessDown + put(0x0000000195L, 0x00000c0083L); // mediaLast + put(0x00000000aeL, 0x00000c0094L); // exit + put(0x0000000192L, 0x00000c009cL); // channelUp + put(0x0000000193L, 0x00000c009dL); // channelDown + put(0x00000000c8L, 0x00000c00b0L); // mediaPlay + put(0x00000000cfL, 0x00000c00b0L); // mediaPlay + put(0x00000000c9L, 0x00000c00b1L); // mediaPause + put(0x00000000a7L, 0x00000c00b2L); // mediaRecord + put(0x00000000d0L, 0x00000c00b3L); // mediaFastForward + put(0x00000000a8L, 0x00000c00b4L); // mediaRewind + put(0x00000000a3L, 0x00000c00b5L); // mediaTrackNext + put(0x00000000a5L, 0x00000c00b6L); // mediaTrackPrevious + put(0x0000000080L, 0x00000c00b7L); // mediaStop + put(0x00000000a6L, 0x00000c00b7L); // mediaStop + put(0x00000000a1L, 0x00000c00b8L); // eject + put(0x00000000a2L, 0x00000c00b8L); // eject + put(0x00000000a4L, 0x00000c00cdL); // mediaPlayPause + put(0x00000000d1L, 0x00000c00e5L); // bassBoost + put(0x000000009bL, 0x00000c018aL); // launchMail + put(0x00000000d7L, 0x00000c018aL); // launchMail + put(0x00000001adL, 0x00000c018dL); // launchContacts + put(0x000000018dL, 0x00000c018eL); // launchCalendar + put(0x0000000247L, 0x00000c01cbL); // launchAssistant + put(0x00000000b5L, 0x00000c0201L); // newKey + put(0x00000000a0L, 0x00000c0203L); // close + put(0x00000000ceL, 0x00000c0203L); // close + put(0x00000000d2L, 0x00000c0208L); // print + put(0x00000000d9L, 0x00000c0221L); // browserSearch + put(0x000000009fL, 0x00000c0225L); // browserForward + put(0x000000009cL, 0x00000c022aL); // browserFavorites + put(0x00000000b6L, 0x00000c0279L); // redo + put(0x0000000100L, 0x000005ff01L); // gameButton1 + put(0x0000000120L, 0x000005ff01L); // gameButton1 + put(0x0000000101L, 0x000005ff02L); // gameButton2 + put(0x0000000121L, 0x000005ff02L); // gameButton2 + put(0x0000000102L, 0x000005ff03L); // gameButton3 + put(0x0000000122L, 0x000005ff03L); // gameButton3 + put(0x0000000103L, 0x000005ff04L); // gameButton4 + put(0x0000000123L, 0x000005ff04L); // gameButton4 + put(0x0000000104L, 0x000005ff05L); // gameButton5 + put(0x0000000124L, 0x000005ff05L); // gameButton5 + put(0x0000000105L, 0x000005ff06L); // gameButton6 + put(0x0000000125L, 0x000005ff06L); // gameButton6 + put(0x0000000106L, 0x000005ff07L); // gameButton7 + put(0x0000000126L, 0x000005ff07L); // gameButton7 + put(0x0000000107L, 0x000005ff08L); // gameButton8 + put(0x0000000127L, 0x000005ff08L); // gameButton8 + put(0x0000000108L, 0x000005ff09L); // gameButton9 + put(0x0000000128L, 0x000005ff09L); // gameButton9 + put(0x0000000109L, 0x000005ff0aL); // gameButton10 + put(0x0000000129L, 0x000005ff0aL); // gameButton10 + put(0x000000010aL, 0x000005ff0bL); // gameButton11 + put(0x000000012aL, 0x000005ff0bL); // gameButton11 + put(0x000000010bL, 0x000005ff0cL); // gameButton12 + put(0x000000012bL, 0x000005ff0cL); // gameButton12 + put(0x000000010cL, 0x000005ff0dL); // gameButton13 + put(0x000000012cL, 0x000005ff0dL); // gameButton13 + put(0x000000010dL, 0x000005ff0eL); // gameButton14 + put(0x000000012dL, 0x000005ff0eL); // gameButton14 + put(0x000000010eL, 0x000005ff0fL); // gameButton15 + put(0x000000012eL, 0x000005ff0fL); // gameButton15 + put(0x000000010fL, 0x000005ff10L); // gameButton16 + put(0x000000012fL, 0x000005ff10L); // gameButton16 + put(0x0000000130L, 0x000005ff11L); // gameButtonA + put(0x0000000131L, 0x000005ff12L); // gameButtonB + put(0x0000000132L, 0x000005ff13L); // gameButtonC + put(0x0000000136L, 0x000005ff14L); // gameButtonLeft1 + put(0x0000000138L, 0x000005ff15L); // gameButtonLeft2 + put(0x000000013cL, 0x000005ff16L); // gameButtonMode + put(0x0000000137L, 0x000005ff17L); // gameButtonRight1 + put(0x0000000139L, 0x000005ff18L); // gameButtonRight2 + put(0x000000013aL, 0x000005ff19L); // gameButtonSelect + put(0x000000013bL, 0x000005ff1aL); // gameButtonStart + put(0x000000013dL, 0x000005ff1bL); // gameButtonThumbLeft + put(0x000000013eL, 0x000005ff1cL); // gameButtonThumbRight + put(0x0000000133L, 0x000005ff1dL); // gameButtonX + put(0x0000000134L, 0x000005ff1eL); // gameButtonY + put(0x0000000135L, 0x000005ff1fL); // gameButtonZ + put(0x00000001d0L, 0x0000000012L); // fn + } + }; + + public static final HashMap keyCodeToLogical = new HashMap() { + private static final long serialVersionUID = 1L; + { + put(0x0000000000L, 0x0000000000L); // none + put(0x0000000043L, 0x0000000008L); // backspace + put(0x000000003dL, 0x0000000009L); // tab + put(0x0000000042L, 0x000000000dL); // enter + put(0x000000006fL, 0x000000001bL); // escape + put(0x000000003eL, 0x0000000020L); // space + put(0x000000004bL, 0x0000000022L); // quote + put(0x0000000037L, 0x000000002cL); // comma + put(0x0000000045L, 0x000000002dL); // minus + put(0x0000000038L, 0x000000002eL); // period + put(0x000000004cL, 0x000000002fL); // slash + put(0x0000000007L, 0x0000000030L); // digit0 + put(0x0000000008L, 0x0000000031L); // digit1 + put(0x0000000009L, 0x0000000032L); // digit2 + put(0x000000000aL, 0x0000000033L); // digit3 + put(0x000000000bL, 0x0000000034L); // digit4 + put(0x000000000cL, 0x0000000035L); // digit5 + put(0x000000000dL, 0x0000000036L); // digit6 + put(0x000000000eL, 0x0000000037L); // digit7 + put(0x000000000fL, 0x0000000038L); // digit8 + put(0x0000000010L, 0x0000000039L); // digit9 + put(0x000000004aL, 0x000000003bL); // semicolon + put(0x0000000046L, 0x000000003dL); // equal + put(0x0000000047L, 0x000000005bL); // bracketLeft + put(0x0000000049L, 0x000000005cL); // backslash + put(0x0000000048L, 0x000000005dL); // bracketRight + put(0x0000000044L, 0x0000000060L); // backquote + put(0x000000001dL, 0x0000000061L); // keyA + put(0x000000001eL, 0x0000000062L); // keyB + put(0x000000001fL, 0x0000000063L); // keyC + put(0x0000000020L, 0x0000000064L); // keyD + put(0x0000000021L, 0x0000000065L); // keyE + put(0x0000000022L, 0x0000000066L); // keyF + put(0x0000000023L, 0x0000000067L); // keyG + put(0x0000000024L, 0x0000000068L); // keyH + put(0x0000000025L, 0x0000000069L); // keyI + put(0x0000000026L, 0x000000006aL); // keyJ + put(0x0000000027L, 0x000000006bL); // keyK + put(0x0000000028L, 0x000000006cL); // keyL + put(0x0000000029L, 0x000000006dL); // keyM + put(0x000000002aL, 0x000000006eL); // keyN + put(0x000000002bL, 0x000000006fL); // keyO + put(0x000000002cL, 0x0000000070L); // keyP + put(0x000000002dL, 0x0000000071L); // keyQ + put(0x000000002eL, 0x0000000072L); // keyR + put(0x000000002fL, 0x0000000073L); // keyS + put(0x0000000030L, 0x0000000074L); // keyT + put(0x0000000031L, 0x0000000075L); // keyU + put(0x0000000032L, 0x0000000076L); // keyV + put(0x0000000033L, 0x0000000077L); // keyW + put(0x0000000034L, 0x0000000078L); // keyX + put(0x0000000035L, 0x0000000079L); // keyY + put(0x0000000036L, 0x000000007aL); // keyZ + put(0x0000000070L, 0x000000007fL); // delete + put(0x0000000073L, 0x0000000104L); // capsLock + put(0x0000000077L, 0x0000000106L); // fn + put(0x000000008fL, 0x000000010aL); // numLock + put(0x0000000074L, 0x000000010cL); // scrollLock + put(0x000000003fL, 0x000000010fL); // symbol + put(0x0000000014L, 0x0000000301L); // arrowDown + put(0x0000000015L, 0x0000000302L); // arrowLeft + put(0x0000000016L, 0x0000000303L); // arrowRight + put(0x0000000013L, 0x0000000304L); // arrowUp + put(0x000000007bL, 0x0000000305L); // end + put(0x000000007aL, 0x0000000306L); // home + put(0x000000005dL, 0x0000000307L); // pageDown + put(0x000000005cL, 0x0000000308L); // pageUp + put(0x000000001cL, 0x0000000401L); // clear + put(0x0000000116L, 0x0000000402L); // copy + put(0x0000000115L, 0x0000000404L); // cut + put(0x000000007cL, 0x0000000407L); // insert + put(0x0000000117L, 0x0000000408L); // paste + put(0x0000000052L, 0x0000000505L); // contextMenu + put(0x0000000103L, 0x0000000508L); // help + put(0x0000000079L, 0x0000000509L); // pause + put(0x0000000017L, 0x000000050cL); // select + put(0x00000000a8L, 0x000000050dL); // zoomIn + put(0x00000000a9L, 0x000000050eL); // zoomOut + put(0x00000000dcL, 0x0000000601L); // brightnessDown + put(0x00000000ddL, 0x0000000602L); // brightnessUp + put(0x000000001bL, 0x0000000603L); // camera + put(0x0000000081L, 0x0000000604L); // eject + put(0x000000001aL, 0x0000000606L); // power + put(0x0000000078L, 0x0000000608L); // printScreen + put(0x00000000e0L, 0x000000060bL); // wakeUp + put(0x00000000d6L, 0x0000000705L); // convert + put(0x00000000ccL, 0x0000000709L); // groupNext + put(0x000000005fL, 0x000000070bL); // modeChange + put(0x00000000d5L, 0x000000070dL); // nonConvert + put(0x00000000d4L, 0x0000000714L); // eisu + put(0x00000000d7L, 0x0000000717L); // hiraganaKatakana + put(0x00000000daL, 0x0000000719L); // kanjiMode + put(0x00000000d3L, 0x000000071dL); // zenkakuHankaku + put(0x0000000083L, 0x0000000801L); // f1 + put(0x0000000084L, 0x0000000802L); // f2 + put(0x0000000085L, 0x0000000803L); // f3 + put(0x0000000086L, 0x0000000804L); // f4 + put(0x0000000087L, 0x0000000805L); // f5 + put(0x0000000088L, 0x0000000806L); // f6 + put(0x0000000089L, 0x0000000807L); // f7 + put(0x000000008aL, 0x0000000808L); // f8 + put(0x000000008bL, 0x0000000809L); // f9 + put(0x000000008cL, 0x000000080aL); // f10 + put(0x000000008dL, 0x000000080bL); // f11 + put(0x000000008eL, 0x000000080cL); // f12 + put(0x0000000080L, 0x0000000a01L); // close + put(0x0000000055L, 0x0000000a05L); // mediaPlayPause + put(0x0000000056L, 0x0000000a07L); // mediaStop + put(0x0000000057L, 0x0000000a08L); // mediaTrackNext + put(0x0000000058L, 0x0000000a09L); // mediaTrackPrevious + put(0x0000000019L, 0x0000000a0fL); // audioVolumeDown + put(0x0000000018L, 0x0000000a10L); // audioVolumeUp + put(0x00000000a4L, 0x0000000a11L); // audioVolumeMute + put(0x00000000d0L, 0x0000000b02L); // launchCalendar + put(0x0000000041L, 0x0000000b03L); // launchMail + put(0x00000000d1L, 0x0000000b05L); // launchMusicPlayer + put(0x0000000040L, 0x0000000b09L); // launchWebBrowser + put(0x00000000cfL, 0x0000000b0cL); // launchContacts + put(0x00000000dbL, 0x0000000b0eL); // launchAssistant + put(0x00000000aeL, 0x0000000c02L); // browserFavorites + put(0x000000007dL, 0x0000000c03L); // browserForward + put(0x0000000054L, 0x0000000c06L); // browserSearch + put(0x00000000b6L, 0x0000000d08L); // avrInput + put(0x00000000b5L, 0x0000000d09L); // avrPower + put(0x00000000a7L, 0x0000000d0aL); // channelDown + put(0x00000000a6L, 0x0000000d0bL); // channelUp + put(0x00000000b7L, 0x0000000d0cL); // colorF0Red + put(0x00000000b8L, 0x0000000d0dL); // colorF1Green + put(0x00000000b9L, 0x0000000d0eL); // colorF2Yellow + put(0x00000000baL, 0x0000000d0fL); // colorF3Blue + put(0x00000000afL, 0x0000000d12L); // closedCaptionToggle + put(0x00000000acL, 0x0000000d22L); // guide + put(0x00000000a5L, 0x0000000d25L); // info + put(0x000000005aL, 0x0000000d2cL); // mediaFastForward + put(0x00000000e5L, 0x0000000d2dL); // mediaLast + put(0x000000007fL, 0x0000000d2eL); // mediaPause + put(0x000000007eL, 0x0000000d2fL); // mediaPlay + put(0x0000000082L, 0x0000000d30L); // mediaRecord + put(0x0000000059L, 0x0000000d31L); // mediaRewind + put(0x00000000b0L, 0x0000000d43L); // settings + put(0x00000000b4L, 0x0000000d45L); // stbInput + put(0x00000000b3L, 0x0000000d46L); // stbPower + put(0x00000000e9L, 0x0000000d48L); // teletext + put(0x00000000aaL, 0x0000000d49L); // tv + put(0x00000000b2L, 0x0000000d4aL); // tvInput + put(0x00000000b1L, 0x0000000d4bL); // tvPower + put(0x00000000ffL, 0x0000000d4eL); // zoomToggle + put(0x00000000adL, 0x0000000d4fL); // dvr + put(0x00000000deL, 0x0000000d50L); // mediaAudioTrack + put(0x0000000111L, 0x0000000d51L); // mediaSkipBackward + put(0x0000000110L, 0x0000000d52L); // mediaSkipForward + put(0x0000000113L, 0x0000000d53L); // mediaStepBackward + put(0x0000000112L, 0x0000000d54L); // mediaStepForward + put(0x00000000e2L, 0x0000000d55L); // mediaTopMenu + put(0x0000000106L, 0x0000000d56L); // navigateIn + put(0x0000000105L, 0x0000000d57L); // navigateNext + put(0x0000000107L, 0x0000000d58L); // navigateOut + put(0x0000000104L, 0x0000000d59L); // navigatePrevious + put(0x00000000e1L, 0x0000000d5aL); // pairing + put(0x000000005bL, 0x0000000e09L); // microphoneVolumeMute + put(0x00000000bbL, 0x0000001001L); // appSwitch + put(0x0000000005L, 0x0000001002L); // call + put(0x0000000050L, 0x0000001003L); // cameraFocus + put(0x0000000006L, 0x0000001004L); // endCall + put(0x0000000004L, 0x0000001005L); // goBack + put(0x0000000003L, 0x0000001006L); // goHome + put(0x000000004fL, 0x0000001007L); // headsetHook + put(0x0000000053L, 0x0000001009L); // notification + put(0x00000000cdL, 0x000000100aL); // mannerMode + put(0x00000000f2L, 0x0000001102L); // tvAntennaCable + put(0x00000000fcL, 0x0000001103L); // tvAudioDescription + put(0x00000000feL, 0x0000001104L); // tvAudioDescriptionMixDown + put(0x00000000fdL, 0x0000001105L); // tvAudioDescriptionMixUp + put(0x0000000100L, 0x0000001106L); // tvContentsMenu + put(0x00000000e6L, 0x0000001107L); // tvDataService + put(0x00000000f9L, 0x0000001108L); // tvInputComponent1 + put(0x00000000faL, 0x0000001109L); // tvInputComponent2 + put(0x00000000f7L, 0x000000110aL); // tvInputComposite1 + put(0x00000000f8L, 0x000000110bL); // tvInputComposite2 + put(0x00000000f1L, 0x0000001112L); // tvNetwork + put(0x00000000eaL, 0x0000001113L); // tvNumberEntry + put(0x00000000e8L, 0x0000001114L); // tvRadioService + put(0x00000000edL, 0x0000001115L); // tvSatellite + put(0x00000000f0L, 0x0000001118L); // tvSatelliteToggle + put(0x00000000ebL, 0x0000001119L); // tvTerrestrialAnalog + put(0x00000000ecL, 0x000000111aL); // tvTerrestrialDigital + put(0x0000000102L, 0x000000111bL); // tvTimer + put(0x00000000bcL, 0x000005ff01L); // gameButton1 + put(0x00000000bdL, 0x000005ff02L); // gameButton2 + put(0x00000000beL, 0x000005ff03L); // gameButton3 + put(0x00000000bfL, 0x000005ff04L); // gameButton4 + put(0x00000000c0L, 0x000005ff05L); // gameButton5 + put(0x00000000c1L, 0x000005ff06L); // gameButton6 + put(0x00000000c2L, 0x000005ff07L); // gameButton7 + put(0x00000000c3L, 0x000005ff08L); // gameButton8 + put(0x00000000c4L, 0x000005ff09L); // gameButton9 + put(0x00000000c5L, 0x000005ff0aL); // gameButton10 + put(0x00000000c6L, 0x000005ff0bL); // gameButton11 + put(0x00000000c7L, 0x000005ff0cL); // gameButton12 + put(0x00000000c8L, 0x000005ff0dL); // gameButton13 + put(0x00000000c9L, 0x000005ff0eL); // gameButton14 + put(0x00000000caL, 0x000005ff0fL); // gameButton15 + put(0x00000000cbL, 0x000005ff10L); // gameButton16 + put(0x0000000060L, 0x000005ff11L); // gameButtonA + put(0x0000000061L, 0x000005ff12L); // gameButtonB + put(0x0000000062L, 0x000005ff13L); // gameButtonC + put(0x0000000066L, 0x000005ff14L); // gameButtonLeft1 + put(0x0000000068L, 0x000005ff15L); // gameButtonLeft2 + put(0x000000006eL, 0x000005ff16L); // gameButtonMode + put(0x0000000067L, 0x000005ff17L); // gameButtonRight1 + put(0x0000000069L, 0x000005ff18L); // gameButtonRight2 + put(0x000000006dL, 0x000005ff19L); // gameButtonSelect + put(0x000000006cL, 0x000005ff1aL); // gameButtonStart + put(0x000000006aL, 0x000005ff1bL); // gameButtonThumbLeft + put(0x000000006bL, 0x000005ff1cL); // gameButtonThumbRight + put(0x0000000063L, 0x000005ff1dL); // gameButtonX + put(0x0000000064L, 0x000005ff1eL); // gameButtonY + put(0x0000000065L, 0x000005ff1fL); // gameButtonZ + put(0x00000000dfL, 0x0100010082L); // sleep + put(0x00000000d9L, 0x0100070087L); // intlRo + put(0x00000000d8L, 0x0100070089L); // intlYen + put(0x00000000a0L, 0x020000000dL); // numpadEnter + put(0x00000000a2L, 0x0200000028L); // numpadParenLeft + put(0x00000000a3L, 0x0200000029L); // numpadParenRight + put(0x000000009bL, 0x020000002aL); // numpadMultiply + put(0x000000009dL, 0x020000002bL); // numpadAdd + put(0x000000009fL, 0x020000002cL); // numpadComma + put(0x000000009cL, 0x020000002dL); // numpadSubtract + put(0x000000009eL, 0x020000002eL); // numpadDecimal + put(0x000000009aL, 0x020000002fL); // numpadDivide + put(0x0000000090L, 0x0200000030L); // numpad0 + put(0x0000000091L, 0x0200000031L); // numpad1 + put(0x0000000092L, 0x0200000032L); // numpad2 + put(0x0000000093L, 0x0200000033L); // numpad3 + put(0x0000000094L, 0x0200000034L); // numpad4 + put(0x0000000095L, 0x0200000035L); // numpad5 + put(0x0000000096L, 0x0200000036L); // numpad6 + put(0x0000000097L, 0x0200000037L); // numpad7 + put(0x0000000098L, 0x0200000038L); // numpad8 + put(0x0000000099L, 0x0200000039L); // numpad9 + put(0x00000000a1L, 0x020000003dL); // numpadEqual + put(0x0000000039L, 0x0300000102L); // altLeft + put(0x0000000071L, 0x0300000105L); // controlLeft + put(0x0000000075L, 0x0300000109L); // metaLeft + put(0x000000003bL, 0x030000010dL); // shiftLeft + put(0x000000003aL, 0x0400000102L); // altRight + put(0x0000000072L, 0x0400000105L); // controlRight + put(0x0000000076L, 0x0400000109L); // metaRight + put(0x000000003cL, 0x040000010dL); // shiftRight + } + }; +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index c5a12252f5a3a..04971dff6185e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -496,6 +496,19 @@ private native void nativeDispatchPointerDataPacket( long nativePlatformViewId, @NonNull ByteBuffer buffer, int position); // ------ End Touch Interaction Support --- + // ------ Start Key Interaction Support --- + /** Sends a packet of pointer data to Flutter's engine. */ + @UiThread + public void dispatchKeyDataPacket(@NonNull ByteBuffer buffer, int position) { + ensureRunningOnMainThread(); + ensureAttachedToNative(); + nativeDispatchKeyDataPacket(nativePlatformViewId, buffer, position); + } + + private native void nativeDispatchKeyDataPacket( + long nativePlatformViewId, @NonNull ByteBuffer buffer, int position); + // ------ End Key Interaction Support --- + @UiThread public void setPlatformViewsController(@NonNull PlatformViewsController platformViewsController) { ensureRunningOnMainThread(); diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 67570302bad28..80ac76051a619 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -302,6 +302,11 @@ public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) flutterJNI.dispatchPointerDataPacket(buffer, position); } + // TODO(dkwingsmt): describe the native behavior that this invokes + public void dispatchKeyDataPacket(@NonNull ByteBuffer buffer, int position) { + flutterJNI.dispatchKeyDataPacket(buffer, position); + } + // TODO(mattcarroll): describe the native behavior that this invokes private void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper) { flutterJNI.registerTexture(textureId, textureWrapper); diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 5cd0d66403edd..1646b8f1c602d 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -44,6 +44,7 @@ import io.flutter.app.FlutterPluginRegistry; import io.flutter.embedding.android.AndroidKeyProcessor; import io.flutter.embedding.android.AndroidTouchProcessor; +import io.flutter.embedding.android.HardwareKeyboard; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.renderer.SurfaceTextureWrapper; @@ -64,6 +65,8 @@ import io.flutter.plugin.mouse.MouseCursorPlugin; import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.plugin.platform.PlatformViewsController; + +import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -128,6 +131,7 @@ static final class ViewportMetrics { private final LocalizationPlugin mLocalizationPlugin; private final MouseCursorPlugin mMouseCursorPlugin; private final AndroidKeyProcessor androidKeyProcessor; + private final HardwareKeyboard mHardwareKeyboard; private final AndroidTouchProcessor androidTouchProcessor; private AccessibilityBridge mAccessibilityNodeProvider; private final SurfaceHolder.Callback mSurfaceCallback; @@ -235,6 +239,7 @@ public void onPostResume() { } mLocalizationPlugin = new LocalizationPlugin(context, localizationChannel); androidKeyProcessor = new AndroidKeyProcessor(this, keyEventChannel, mTextInputPlugin); + mHardwareKeyboard = new HardwareKeyboard(); androidTouchProcessor = new AndroidTouchProcessor(flutterRenderer, /*trackMotionEvents=*/ false); platformViewsController.attachToFlutterRenderer(flutterRenderer); @@ -268,6 +273,18 @@ public DartExecutor getDartExecutor() { return dartExecutor; } + private boolean handleHardwareEvent(KeyEvent event) { + List keyData = mHardwareKeyboard.convertEvent(event); + boolean handled = true; // TODO + if (keyData != null) { + for (final HardwareKeyboard.KeyDatum keyDatum : keyData) { + final ByteBuffer buffer = mHardwareKeyboard.packDatum(keyDatum); + flutterRenderer.dispatchKeyDataPacket(buffer, buffer.position()); + } + } + return handled; + } + @Override public boolean dispatchKeyEvent(KeyEvent event) { Log.e(TAG, "dispatchKeyEvent: " + event.toString()); @@ -282,7 +299,8 @@ public boolean dispatchKeyEvent(KeyEvent event) { // superclass. The key processor will typically handle all events except // those where it has re-dispatched the event after receiving a reply from // the framework that the framework did not handle it. - return (isAttached() && androidKeyProcessor.onKeyEvent(event)) || super.dispatchKeyEvent(event); + return (isAttached() && (handleHardwareEvent(event) || androidKeyProcessor.onKeyEvent(event))) + || super.dispatchKeyEvent(event); } public FlutterNativeView getFlutterNativeView() { diff --git a/shell/platform/android/keycodes/keyboard_map_android.h b/shell/platform/android/keycodes/keyboard_map_android.h new file mode 100644 index 0000000000000..71a08ba12fd34 --- /dev/null +++ b/shell/platform/android/keycodes/keyboard_map_android.h @@ -0,0 +1,459 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/keyboard_map_android_cc.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +// Maps Android-specific key codes to the matching LogicalKeyboardKey id. +const std::map g_android_to_logical_key = { + { 0, 0x0100000000 }, // none + { 223, 0x0100010082 }, // sleep + { 224, 0x0100010083 }, // wakeUp + { 29, 0x0000000061 }, // keyA + { 30, 0x0000000062 }, // keyB + { 31, 0x0000000063 }, // keyC + { 32, 0x0000000064 }, // keyD + { 33, 0x0000000065 }, // keyE + { 34, 0x0000000066 }, // keyF + { 35, 0x0000000067 }, // keyG + { 36, 0x0000000068 }, // keyH + { 37, 0x0000000069 }, // keyI + { 38, 0x000000006a }, // keyJ + { 39, 0x000000006b }, // keyK + { 40, 0x000000006c }, // keyL + { 41, 0x000000006d }, // keyM + { 42, 0x000000006e }, // keyN + { 43, 0x000000006f }, // keyO + { 44, 0x0000000070 }, // keyP + { 45, 0x0000000071 }, // keyQ + { 46, 0x0000000072 }, // keyR + { 47, 0x0000000073 }, // keyS + { 48, 0x0000000074 }, // keyT + { 49, 0x0000000075 }, // keyU + { 50, 0x0000000076 }, // keyV + { 51, 0x0000000077 }, // keyW + { 52, 0x0000000078 }, // keyX + { 53, 0x0000000079 }, // keyY + { 54, 0x000000007a }, // keyZ + { 8, 0x0000000031 }, // digit1 + { 9, 0x0000000032 }, // digit2 + { 10, 0x0000000033 }, // digit3 + { 11, 0x0000000034 }, // digit4 + { 12, 0x0000000035 }, // digit5 + { 13, 0x0000000036 }, // digit6 + { 14, 0x0000000037 }, // digit7 + { 15, 0x0000000038 }, // digit8 + { 16, 0x0000000039 }, // digit9 + { 7, 0x0000000030 }, // digit0 + { 66, 0x0100070028 }, // enter + { 111, 0x0100070029 }, // escape + { 67, 0x010007002a }, // backspace + { 61, 0x010007002b }, // tab + { 62, 0x0000000020 }, // space + { 69, 0x000000002d }, // minus + { 70, 0x000000003d }, // equal + { 71, 0x000000005b }, // bracketLeft + { 72, 0x000000005d }, // bracketRight + { 73, 0x000000005c }, // backslash + { 74, 0x000000003b }, // semicolon + { 75, 0x0000000027 }, // quote + { 68, 0x0000000060 }, // backquote + { 55, 0x000000002c }, // comma + { 56, 0x000000002e }, // period + { 76, 0x000000002f }, // slash + { 115, 0x0100070039 }, // capsLock + { 131, 0x010007003a }, // f1 + { 132, 0x010007003b }, // f2 + { 133, 0x010007003c }, // f3 + { 134, 0x010007003d }, // f4 + { 135, 0x010007003e }, // f5 + { 136, 0x010007003f }, // f6 + { 137, 0x0100070040 }, // f7 + { 138, 0x0100070041 }, // f8 + { 139, 0x0100070042 }, // f9 + { 140, 0x0100070043 }, // f10 + { 141, 0x0100070044 }, // f11 + { 142, 0x0100070045 }, // f12 + { 120, 0x0100070046 }, // printScreen + { 116, 0x0100070047 }, // scrollLock + { 121, 0x0100070048 }, // pause + { 124, 0x0100070049 }, // insert + { 122, 0x010007004a }, // home + { 92, 0x010007004b }, // pageUp + { 112, 0x010007004c }, // delete + { 123, 0x010007004d }, // end + { 93, 0x010007004e }, // pageDown + { 22, 0x010007004f }, // arrowRight + { 21, 0x0100070050 }, // arrowLeft + { 20, 0x0100070051 }, // arrowDown + { 19, 0x0100070052 }, // arrowUp + { 143, 0x0100070053 }, // numLock + { 154, 0x0100070054 }, // numpadDivide + { 155, 0x0100070055 }, // numpadMultiply + { 156, 0x0100070056 }, // numpadSubtract + { 157, 0x0100070057 }, // numpadAdd + { 160, 0x0100070058 }, // numpadEnter + { 145, 0x0100070059 }, // numpad1 + { 146, 0x010007005a }, // numpad2 + { 147, 0x010007005b }, // numpad3 + { 148, 0x010007005c }, // numpad4 + { 149, 0x010007005d }, // numpad5 + { 150, 0x010007005e }, // numpad6 + { 151, 0x010007005f }, // numpad7 + { 152, 0x0100070060 }, // numpad8 + { 153, 0x0100070061 }, // numpad9 + { 144, 0x0100070062 }, // numpad0 + { 158, 0x0100070063 }, // numpadDecimal + { 82, 0x0100070065 }, // contextMenu + { 26, 0x0100070066 }, // power + { 161, 0x0100070067 }, // numpadEqual + { 259, 0x0100070075 }, // help + { 23, 0x0100070077 }, // select + { 277, 0x010007007b }, // cut + { 278, 0x010007007c }, // copy + { 279, 0x010007007d }, // paste + { 164, 0x010007007f }, // audioVolumeMute + { 24, 0x0100070080 }, // audioVolumeUp + { 25, 0x0100070081 }, // audioVolumeDown + { 159, 0x0100070085 }, // numpadComma + { 214, 0x010007008a }, // convert + { 213, 0x010007008b }, // nonConvert + { 162, 0x01000700b6 }, // numpadParenLeft + { 163, 0x01000700b7 }, // numpadParenRight + { 113, 0x01000700e0 }, // controlLeft + { 59, 0x01000700e1 }, // shiftLeft + { 57, 0x01000700e2 }, // altLeft + { 117, 0x01000700e3 }, // metaLeft + { 114, 0x01000700e4 }, // controlRight + { 60, 0x01000700e5 }, // shiftRight + { 58, 0x01000700e6 }, // altRight + { 118, 0x01000700e7 }, // metaRight + { 165, 0x01000c0060 }, // info + { 175, 0x01000c0061 }, // closedCaptionToggle + { 221, 0x01000c006f }, // brightnessUp + { 220, 0x01000c0070 }, // brightnessDown + { 229, 0x01000c0083 }, // mediaLast + { 166, 0x01000c009c }, // channelUp + { 167, 0x01000c009d }, // channelDown + { 126, 0x01000c00b0 }, // mediaPlay + { 127, 0x01000c00b1 }, // mediaPause + { 130, 0x01000c00b2 }, // mediaRecord + { 90, 0x01000c00b3 }, // mediaFastForward + { 89, 0x01000c00b4 }, // mediaRewind + { 87, 0x01000c00b5 }, // mediaTrackNext + { 88, 0x01000c00b6 }, // mediaTrackPrevious + { 86, 0x01000c00b7 }, // mediaStop + { 129, 0x01000c00b8 }, // eject + { 85, 0x01000c00cd }, // mediaPlayPause + { 65, 0x01000c018a }, // launchMail + { 207, 0x01000c018d }, // launchContacts + { 208, 0x01000c018e }, // launchCalendar + { 219, 0x01000c01cb }, // launchAssistant + { 128, 0x01000c0203 }, // close + { 84, 0x01000c0221 }, // browserSearch + { 125, 0x01000c0225 }, // browserForward + { 174, 0x01000c022a }, // browserFavorites + { 168, 0x01000c022d }, // zoomIn + { 169, 0x01000c022e }, // zoomOut + { 255, 0x01000c0232 }, // zoomToggle + { 188, 0x010005ff01 }, // gameButton1 + { 189, 0x010005ff02 }, // gameButton2 + { 190, 0x010005ff03 }, // gameButton3 + { 191, 0x010005ff04 }, // gameButton4 + { 192, 0x010005ff05 }, // gameButton5 + { 193, 0x010005ff06 }, // gameButton6 + { 194, 0x010005ff07 }, // gameButton7 + { 195, 0x010005ff08 }, // gameButton8 + { 196, 0x010005ff09 }, // gameButton9 + { 197, 0x010005ff0a }, // gameButton10 + { 198, 0x010005ff0b }, // gameButton11 + { 199, 0x010005ff0c }, // gameButton12 + { 200, 0x010005ff0d }, // gameButton13 + { 201, 0x010005ff0e }, // gameButton14 + { 202, 0x010005ff0f }, // gameButton15 + { 203, 0x010005ff10 }, // gameButton16 + { 96, 0x010005ff11 }, // gameButtonA + { 97, 0x010005ff12 }, // gameButtonB + { 98, 0x010005ff13 }, // gameButtonC + { 102, 0x010005ff14 }, // gameButtonLeft1 + { 104, 0x010005ff15 }, // gameButtonLeft2 + { 110, 0x010005ff16 }, // gameButtonMode + { 103, 0x010005ff17 }, // gameButtonRight1 + { 105, 0x010005ff18 }, // gameButtonRight2 + { 109, 0x010005ff19 }, // gameButtonSelect + { 108, 0x010005ff1a }, // gameButtonStart + { 106, 0x010005ff1b }, // gameButtonThumbLeft + { 107, 0x010005ff1c }, // gameButtonThumbRight + { 99, 0x010005ff1d }, // gameButtonX + { 100, 0x010005ff1e }, // gameButtonY + { 101, 0x010005ff1f }, // gameButtonZ + { 119, 0x0100000012 }, // fn +}; + +// Maps Android-specific scan codes to the matching PhysicalKeyboardKey id (a.k.a. HID USB code). +const std::map g_android_to_physical_key = { + { 205, 0x00000014 }, // suspend + { 142, 0x00010082 }, // sleep + { 143, 0x00010083 }, // wakeUp + { 30, 0x00070004 }, // keyA + { 48, 0x00070005 }, // keyB + { 46, 0x00070006 }, // keyC + { 32, 0x00070007 }, // keyD + { 18, 0x00070008 }, // keyE + { 33, 0x00070009 }, // keyF + { 34, 0x0007000a }, // keyG + { 35, 0x0007000b }, // keyH + { 23, 0x0007000c }, // keyI + { 36, 0x0007000d }, // keyJ + { 37, 0x0007000e }, // keyK + { 38, 0x0007000f }, // keyL + { 50, 0x00070010 }, // keyM + { 49, 0x00070011 }, // keyN + { 24, 0x00070012 }, // keyO + { 25, 0x00070013 }, // keyP + { 16, 0x00070014 }, // keyQ + { 19, 0x00070015 }, // keyR + { 31, 0x00070016 }, // keyS + { 20, 0x00070017 }, // keyT + { 22, 0x00070018 }, // keyU + { 47, 0x00070019 }, // keyV + { 17, 0x0007001a }, // keyW + { 45, 0x0007001b }, // keyX + { 21, 0x0007001c }, // keyY + { 44, 0x0007001d }, // keyZ + { 2, 0x0007001e }, // digit1 + { 3, 0x0007001f }, // digit2 + { 4, 0x00070020 }, // digit3 + { 5, 0x00070021 }, // digit4 + { 6, 0x00070022 }, // digit5 + { 7, 0x00070023 }, // digit6 + { 8, 0x00070024 }, // digit7 + { 9, 0x00070025 }, // digit8 + { 10, 0x00070026 }, // digit9 + { 11, 0x00070027 }, // digit0 + { 28, 0x00070028 }, // enter + { 1, 0x00070029 }, // escape + { 14, 0x0007002a }, // backspace + { 15, 0x0007002b }, // tab + { 57, 0x0007002c }, // space + { 12, 0x0007002d }, // minus + { 13, 0x0007002e }, // equal + { 26, 0x0007002f }, // bracketLeft + { 27, 0x00070030 }, // bracketRight + { 43, 0x00070031 }, // backslash + { 86, 0x00070031 }, // backslash + { 39, 0x00070033 }, // semicolon + { 40, 0x00070034 }, // quote + { 41, 0x00070035 }, // backquote + { 51, 0x00070036 }, // comma + { 52, 0x00070037 }, // period + { 53, 0x00070038 }, // slash + { 58, 0x00070039 }, // capsLock + { 59, 0x0007003a }, // f1 + { 60, 0x0007003b }, // f2 + { 61, 0x0007003c }, // f3 + { 62, 0x0007003d }, // f4 + { 63, 0x0007003e }, // f5 + { 64, 0x0007003f }, // f6 + { 65, 0x00070040 }, // f7 + { 66, 0x00070041 }, // f8 + { 67, 0x00070042 }, // f9 + { 68, 0x00070043 }, // f10 + { 87, 0x00070044 }, // f11 + { 88, 0x00070045 }, // f12 + { 99, 0x00070046 }, // printScreen + { 70, 0x00070047 }, // scrollLock + { 119, 0x00070048 }, // pause + { 411, 0x00070048 }, // pause + { 110, 0x00070049 }, // insert + { 102, 0x0007004a }, // home + { 104, 0x0007004b }, // pageUp + { 177, 0x0007004b }, // pageUp + { 111, 0x0007004c }, // delete + { 107, 0x0007004d }, // end + { 109, 0x0007004e }, // pageDown + { 178, 0x0007004e }, // pageDown + { 106, 0x0007004f }, // arrowRight + { 105, 0x00070050 }, // arrowLeft + { 108, 0x00070051 }, // arrowDown + { 103, 0x00070052 }, // arrowUp + { 69, 0x00070053 }, // numLock + { 98, 0x00070054 }, // numpadDivide + { 55, 0x00070055 }, // numpadMultiply + { 74, 0x00070056 }, // numpadSubtract + { 78, 0x00070057 }, // numpadAdd + { 96, 0x00070058 }, // numpadEnter + { 79, 0x00070059 }, // numpad1 + { 80, 0x0007005a }, // numpad2 + { 81, 0x0007005b }, // numpad3 + { 75, 0x0007005c }, // numpad4 + { 76, 0x0007005d }, // numpad5 + { 77, 0x0007005e }, // numpad6 + { 71, 0x0007005f }, // numpad7 + { 72, 0x00070060 }, // numpad8 + { 73, 0x00070061 }, // numpad9 + { 82, 0x00070062 }, // numpad0 + { 83, 0x00070063 }, // numpadDecimal + { 127, 0x00070065 }, // contextMenu + { 139, 0x00070065 }, // contextMenu + { 116, 0x00070066 }, // power + { 152, 0x00070066 }, // power + { 117, 0x00070067 }, // numpadEqual + { 183, 0x00070068 }, // f13 + { 184, 0x00070069 }, // f14 + { 185, 0x0007006a }, // f15 + { 186, 0x0007006b }, // f16 + { 187, 0x0007006c }, // f17 + { 188, 0x0007006d }, // f18 + { 189, 0x0007006e }, // f19 + { 190, 0x0007006f }, // f20 + { 191, 0x00070070 }, // f21 + { 192, 0x00070071 }, // f22 + { 193, 0x00070072 }, // f23 + { 194, 0x00070073 }, // f24 + { 134, 0x00070074 }, // open + { 138, 0x00070075 }, // help + { 353, 0x00070077 }, // select + { 129, 0x00070079 }, // again + { 131, 0x0007007a }, // undo + { 137, 0x0007007b }, // cut + { 133, 0x0007007c }, // copy + { 135, 0x0007007d }, // paste + { 136, 0x0007007e }, // find + { 113, 0x0007007f }, // audioVolumeMute + { 115, 0x00070080 }, // audioVolumeUp + { 114, 0x00070081 }, // audioVolumeDown + { 95, 0x00070085 }, // numpadComma + { 121, 0x00070085 }, // numpadComma + { 92, 0x0007008a }, // convert + { 94, 0x0007008b }, // nonConvert + { 90, 0x00070092 }, // lang3 + { 91, 0x00070093 }, // lang4 + { 130, 0x000700a3 }, // props + { 179, 0x000700b6 }, // numpadParenLeft + { 180, 0x000700b7 }, // numpadParenRight + { 29, 0x000700e0 }, // controlLeft + { 42, 0x000700e1 }, // shiftLeft + { 56, 0x000700e2 }, // altLeft + { 125, 0x000700e3 }, // metaLeft + { 97, 0x000700e4 }, // controlRight + { 54, 0x000700e5 }, // shiftRight + { 100, 0x000700e6 }, // altRight + { 126, 0x000700e7 }, // metaRight + { 358, 0x000c0060 }, // info + { 370, 0x000c0061 }, // closedCaptionToggle + { 225, 0x000c006f }, // brightnessUp + { 224, 0x000c0070 }, // brightnessDown + { 405, 0x000c0083 }, // mediaLast + { 174, 0x000c0094 }, // exit + { 402, 0x000c009c }, // channelUp + { 403, 0x000c009d }, // channelDown + { 200, 0x000c00b0 }, // mediaPlay + { 207, 0x000c00b0 }, // mediaPlay + { 201, 0x000c00b1 }, // mediaPause + { 167, 0x000c00b2 }, // mediaRecord + { 208, 0x000c00b3 }, // mediaFastForward + { 168, 0x000c00b4 }, // mediaRewind + { 163, 0x000c00b5 }, // mediaTrackNext + { 165, 0x000c00b6 }, // mediaTrackPrevious + { 128, 0x000c00b7 }, // mediaStop + { 166, 0x000c00b7 }, // mediaStop + { 161, 0x000c00b8 }, // eject + { 162, 0x000c00b8 }, // eject + { 164, 0x000c00cd }, // mediaPlayPause + { 209, 0x000c00e5 }, // bassBoost + { 155, 0x000c018a }, // launchMail + { 215, 0x000c018a }, // launchMail + { 429, 0x000c018d }, // launchContacts + { 397, 0x000c018e }, // launchCalendar + { 583, 0x000c01cb }, // launchAssistant + { 181, 0x000c0201 }, // newKey + { 160, 0x000c0203 }, // close + { 206, 0x000c0203 }, // close + { 210, 0x000c0208 }, // print + { 217, 0x000c0221 }, // browserSearch + { 159, 0x000c0225 }, // browserForward + { 156, 0x000c022a }, // browserFavorites + { 182, 0x000c0279 }, // redo + { 256, 0x0005ff01 }, // gameButton1 + { 288, 0x0005ff01 }, // gameButton1 + { 257, 0x0005ff02 }, // gameButton2 + { 289, 0x0005ff02 }, // gameButton2 + { 258, 0x0005ff03 }, // gameButton3 + { 290, 0x0005ff03 }, // gameButton3 + { 259, 0x0005ff04 }, // gameButton4 + { 291, 0x0005ff04 }, // gameButton4 + { 260, 0x0005ff05 }, // gameButton5 + { 292, 0x0005ff05 }, // gameButton5 + { 261, 0x0005ff06 }, // gameButton6 + { 293, 0x0005ff06 }, // gameButton6 + { 262, 0x0005ff07 }, // gameButton7 + { 294, 0x0005ff07 }, // gameButton7 + { 263, 0x0005ff08 }, // gameButton8 + { 295, 0x0005ff08 }, // gameButton8 + { 264, 0x0005ff09 }, // gameButton9 + { 296, 0x0005ff09 }, // gameButton9 + { 265, 0x0005ff0a }, // gameButton10 + { 297, 0x0005ff0a }, // gameButton10 + { 266, 0x0005ff0b }, // gameButton11 + { 298, 0x0005ff0b }, // gameButton11 + { 267, 0x0005ff0c }, // gameButton12 + { 299, 0x0005ff0c }, // gameButton12 + { 268, 0x0005ff0d }, // gameButton13 + { 300, 0x0005ff0d }, // gameButton13 + { 269, 0x0005ff0e }, // gameButton14 + { 301, 0x0005ff0e }, // gameButton14 + { 270, 0x0005ff0f }, // gameButton15 + { 302, 0x0005ff0f }, // gameButton15 + { 271, 0x0005ff10 }, // gameButton16 + { 303, 0x0005ff10 }, // gameButton16 + { 304, 0x0005ff11 }, // gameButtonA + { 305, 0x0005ff12 }, // gameButtonB + { 306, 0x0005ff13 }, // gameButtonC + { 310, 0x0005ff14 }, // gameButtonLeft1 + { 312, 0x0005ff15 }, // gameButtonLeft2 + { 316, 0x0005ff16 }, // gameButtonMode + { 311, 0x0005ff17 }, // gameButtonRight1 + { 313, 0x0005ff18 }, // gameButtonRight2 + { 314, 0x0005ff19 }, // gameButtonSelect + { 315, 0x0005ff1a }, // gameButtonStart + { 317, 0x0005ff1b }, // gameButtonThumbLeft + { 318, 0x0005ff1c }, // gameButtonThumbRight + { 307, 0x0005ff1d }, // gameButtonX + { 308, 0x0005ff1e }, // gameButtonY + { 309, 0x0005ff1f }, // gameButtonZ + { 464, 0x00000012 }, // fn +}; + +// A map of Android key codes which have printable representations, but appear +// on the number pad. Used to provide different key objects for keys like +// KEY_EQUALS and NUMPAD_EQUALS. Maps Android key codes to PhysicalKeyboardKey +// codes (USB HID codes). +const std::map g_android_numpad_map = { + { 154, 0x0100070054 }, // numpadDivide + { 155, 0x0100070055 }, // numpadMultiply + { 156, 0x0100070056 }, // numpadSubtract + { 157, 0x0100070057 }, // numpadAdd + { 145, 0x0100070059 }, // numpad1 + { 146, 0x010007005a }, // numpad2 + { 147, 0x010007005b }, // numpad3 + { 148, 0x010007005c }, // numpad4 + { 149, 0x010007005d }, // numpad5 + { 150, 0x010007005e }, // numpad6 + { 151, 0x010007005f }, // numpad7 + { 152, 0x0100070060 }, // numpad8 + { 153, 0x0100070061 }, // numpad9 + { 144, 0x0100070062 }, // numpad0 + { 158, 0x0100070063 }, // numpadDecimal + { 161, 0x0100070067 }, // numpadEqual + { 159, 0x0100070085 }, // numpadComma + { 162, 0x01000700b6 }, // numpadParenLeft + { 163, 0x01000700b7 }, // numpadParenRight +}; diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index efe994f52d94b..f2628c81e82da 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -395,6 +395,17 @@ static void DispatchPointerDataPacket(JNIEnv* env, std::move(packet)); } +static void DispatchKeyDataPacket(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jobject buffer, + jint position) { + uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); + auto packet = std::make_unique(data, position); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchKeyDataPacket( + std::move(packet)); +} + static void DispatchSemanticsAction(JNIEnv* env, jobject jcaller, jlong shell_holder, @@ -669,6 +680,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "(JLjava/nio/ByteBuffer;I)V", .fnPtr = reinterpret_cast(&DispatchPointerDataPacket), }, + { + .name = "nativeDispatchKeyDataPacket", + .signature = "(JLjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&DispatchKeyDataPacket), + }, { .name = "nativeDispatchSemanticsAction", .signature = "(JIILjava/nio/ByteBuffer;I)V", diff --git a/shell/platform/android/test/io/flutter/embedding/android/HardwareKeyboardTest.java b/shell/platform/android/test/io/flutter/embedding/android/HardwareKeyboardTest.java new file mode 100644 index 0000000000000..1f8b41e2eb0e1 --- /dev/null +++ b/shell/platform/android/test/io/flutter/embedding/android/HardwareKeyboardTest.java @@ -0,0 +1,57 @@ +package io.flutter.embedding.android; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.notNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.TargetApi; +import android.view.KeyEvent; +import android.view.View; +import androidx.annotation.NonNull; +import io.flutter.embedding.android.HardwareKeyboard; +import io.flutter.util.FakeKeyEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +@TargetApi(28) +public class HardwareKeyboardTest { + @Mock FlutterJNI mockFlutterJni; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mockFlutterJni.isAttached()).thenReturn(true); + } + + @Test + public void convertPressHoldReleaseSingleKeyTest() { + final HardwareKeyboard keyboard = new HardwareKeyboard(); + + keyboard.convertEvent(new KeyEvent( + 100, // downTime + 100, // eventTime + ACTION_DOWN, // action + 100, // code + 0, // repeat + 0, // metaState + 0, // deviceId + 100, // scanCode + flags // scanCode + )); + } +} diff --git a/tools/android_lint/project.xml b/tools/android_lint/project.xml index f557fb58e130c..a92bd504f4e68 100644 --- a/tools/android_lint/project.xml +++ b/tools/android_lint/project.xml @@ -20,6 +20,8 @@ + +