mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Recommended implementation of combining characters implementation. (#7758)
This commit is contained in:
parent
d521212719
commit
4663d357a3
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user