diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7d5e643fdcb..cea4f342c63 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -440,6 +440,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActi FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/android/AndroidKeyProcessor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 7779ecef835..b6560fca387 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -108,6 +108,7 @@ java_library("flutter_shell_java") { "io/flutter/app/FlutterPluginRegistry.java", "io/flutter/embedding/engine/FlutterEngine.java", "io/flutter/embedding/engine/FlutterJNI.java", + "io/flutter/embedding/engine/android/AndroidKeyProcessor.java", "io/flutter/embedding/engine/dart/DartExecutor.java", "io/flutter/embedding/engine/dart/DartMessenger.java", "io/flutter/embedding/engine/dart/PlatformMessageHandler.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/android/AndroidKeyProcessor.java b/shell/platform/android/io/flutter/embedding/engine/android/AndroidKeyProcessor.java new file mode 100644 index 00000000000..0a876cec5e2 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/android/AndroidKeyProcessor.java @@ -0,0 +1,87 @@ +package io.flutter.embedding.engine.android; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; + +import io.flutter.embedding.engine.systemchannels.KeyEventChannel; + +public class AndroidKeyProcessor { + private final KeyEventChannel keyEventChannel; + private int combiningCharacter; + + public AndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel) { + this.keyEventChannel = keyEventChannel; + } + + public void onKeyUp(@NonNull KeyEvent keyEvent) { + Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar()); + keyEventChannel.keyUp( + new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter) + ); + } + + public void onKeyDown(@NonNull KeyEvent keyEvent) { + Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar()); + keyEventChannel.keyDown( + new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter) + ); + } + + /** + * Applies the given Unicode character in {@code newCharacterCodePoint} to a previously + * entered Unicode combining character and returns the combination of these characters + * if a combination exists. + *
+ * This method mutates {@link #combiningCharacter} over time to combine characters. + *
+ * One of the following things happens in this method: + *
+ * The following reference explains the concept of a "combining character":
+ * https://en.wikipedia.org/wiki/Combining_character
+ */
+ @Nullable
+ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) {
+ if (newCharacterCodePoint == 0) {
+ return null;
+ }
+
+ Character complexCharacter = (char) newCharacterCodePoint;
+ boolean isNewCodePointACombiningCharacter = (newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
+ if (isNewCodePointACombiningCharacter) {
+ // If a combining character was entered before, combine this one with that one.
+ int plainCodePoint = newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT_MASK;
+ if (combiningCharacter != 0) {
+ combiningCharacter = KeyCharacterMap.getDeadChar(combiningCharacter, plainCodePoint);
+ } else {
+ combiningCharacter = plainCodePoint;
+ }
+ } else {
+ // The new character is a regular character. Apply combiningCharacter to it, if it exists.
+ if (combiningCharacter != 0) {
+ int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint);
+ if (combinedChar > 0) {
+ complexCharacter = (char) combinedChar;
+ }
+ combiningCharacter = 0;
+ }
+ }
+
+ return complexCharacter;
+ }
+}
diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
index 2ecd048386f..5b90d2a761d 100644
--- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
+++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
@@ -5,6 +5,7 @@
package io.flutter.embedding.engine.systemchannels;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.view.KeyEvent;
import java.util.HashMap;
@@ -26,7 +27,7 @@ public class KeyEventChannel {
this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/keyevent", JSONMessageCodec.INSTANCE);
}
- public void keyUp(@NonNull KeyEvent keyEvent) {
+ public void keyUp(@NonNull FlutterKeyEvent keyEvent) {
Map