Recommended implementation of combining characters implementation. (#7758)

This commit is contained in:
Matt Carroll 2019-02-11 15:27:55 -08:00 committed by GitHub
parent d521212719
commit 4663d357a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 160 additions and 11 deletions

View File

@ -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

View File

@ -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",

View File

@ -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.
* <p>
* This method mutates {@link #combiningCharacter} over time to combine characters.
* <p>
* One of the following things happens in this method:
* <ul>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then {@code newCharacterCodePoint} is returned.</li>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is a combining character, then {@code newCharacterCodePoint} is saved as the
* {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is also a combining character, then the {@code newCharacterCodePoint} is combined with
* the existing {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then the {@link #combiningCharacter} is applied to the
* regular {@code newCharacterCodePoint} and the resulting complex character is returned. The
* {@link #combiningCharacter} is cleared.</li>
* </ul>
* <p>
* 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;
}
}

View File

@ -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<String, Object> message = new HashMap<>();
message.put("type", "keyup");
message.put("keymap", "android");
@ -35,7 +36,7 @@ public class KeyEventChannel {
channel.send(message);
}
public void keyDown(@NonNull KeyEvent keyEvent) {
public void keyDown(@NonNull FlutterKeyEvent keyEvent) {
Map<String, Object> message = new HashMap<>();
message.put("type", "keydown");
message.put("keymap", "android");
@ -44,11 +45,68 @@ public class KeyEventChannel {
channel.send(message);
}
private void encodeKeyEvent(@NonNull KeyEvent event, @NonNull Map<String, Object> message) {
message.put("flags", event.getFlags());
message.put("codePoint", event.getUnicodeChar());
message.put("keyCode", event.getKeyCode());
message.put("scanCode", event.getScanCode());
message.put("metaState", event.getMetaState());
private void encodeKeyEvent(@NonNull FlutterKeyEvent event, @NonNull Map<String, Object> message) {
message.put("flags", event.flags);
message.put("plainCodePoint", event.plainCodePoint);
message.put("codePoint", event.codePoint);
message.put("keyCode", event.keyCode);
message.put("scanCode", event.scanCode);
message.put("metaState", event.metaState);
if (event.complexCharacter != null) {
message.put("character", event.complexCharacter.toString());
}
}
/**
* Key event as defined by Flutter.
*/
public static class FlutterKeyEvent {
public final int flags;
public final int plainCodePoint;
public final int codePoint;
public final int keyCode;
@Nullable
public final Character complexCharacter;
public final int scanCode;
public final int metaState;
public FlutterKeyEvent(
@NonNull KeyEvent androidKeyEvent
) {
this(androidKeyEvent, null);
}
public FlutterKeyEvent(
@NonNull KeyEvent androidKeyEvent,
@Nullable Character complexCharacter
) {
this(
androidKeyEvent.getFlags(),
androidKeyEvent.getUnicodeChar(0x0),
androidKeyEvent.getUnicodeChar(),
androidKeyEvent.getKeyCode(),
complexCharacter,
androidKeyEvent.getScanCode(),
androidKeyEvent.getMetaState()
);
}
public FlutterKeyEvent(
int flags,
int plainCodePoint,
int codePoint,
int keyCode,
@Nullable Character complexCharacter,
int scanCode,
int metaState
) {
this.flags = flags;
this.plainCodePoint = plainCodePoint;
this.codePoint = codePoint;
this.keyCode = keyCode;
this.complexCharacter = complexCharacter;
this.scanCode = scanCode;
this.metaState = metaState;
}
}
}

View File

@ -26,6 +26,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import io.flutter.app.FlutterPluginRegistry;
import io.flutter.embedding.engine.android.AndroidKeyProcessor;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
@ -93,6 +94,7 @@ public class FlutterView extends SurfaceView
private final SystemChannel systemChannel;
private final InputMethodManager mImm;
private final TextInputPlugin mTextInputPlugin;
private final AndroidKeyProcessor androidKeyProcessor;
private final SurfaceHolder.Callback mSurfaceCallback;
private final ViewportMetrics mMetrics;
private final AccessibilityManager mAccessibilityManager;
@ -172,7 +174,7 @@ public class FlutterView extends SurfaceView
addActivityLifecycleListener(platformPlugin);
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mTextInputPlugin = new TextInputPlugin(this);
androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel);
setLocales(getResources().getConfiguration());
sendUserPlatformSettingsToDart();
@ -183,7 +185,7 @@ public class FlutterView extends SurfaceView
if (!isAttached()) {
return super.onKeyUp(keyCode, event);
}
keyEventChannel.keyUp(event);
androidKeyProcessor.onKeyUp(event);
return super.onKeyUp(keyCode, event);
}
@ -199,7 +201,7 @@ public class FlutterView extends SurfaceView
}
}
keyEventChannel.keyDown(event);
androidKeyProcessor.onKeyDown(event);
return super.onKeyDown(keyCode, event);
}