Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recommended implementation of combining characters implementation. #7758

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,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
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,7 +27,7 @@ public KeyEventChannel(@NonNull DartExecutor dartExecutor) {
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");
Expand All @@ -35,7 +36,7 @@ public void keyUp(@NonNull KeyEvent keyEvent) {
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");
Expand All @@ -44,11 +45,68 @@ public void keyDown(@NonNull KeyEvent keyEvent) {
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(),
gspencergoog marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
}
8 changes: 5 additions & 3 deletions shell/platform/android/io/flutter/view/FlutterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
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;
Expand Down Expand Up @@ -93,6 +94,7 @@ static final class ViewportMetrics {
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;
Expand Down Expand Up @@ -172,7 +174,7 @@ public void surfaceDestroyed(SurfaceHolder holder) {
addActivityLifecycleListener(platformPlugin);
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mTextInputPlugin = new TextInputPlugin(this);
gspencergoog marked this conversation as resolved.
Show resolved Hide resolved

androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel);

setLocales(getResources().getConfiguration());
sendUserPlatformSettingsToDart();
Expand All @@ -183,7 +185,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
if (!isAttached()) {
return super.onKeyUp(keyCode, event);
}
keyEventChannel.keyUp(event);
androidKeyProcessor.onKeyUp(event);
return super.onKeyUp(keyCode, event);
}

Expand All @@ -199,7 +201,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
}
}

keyEventChannel.keyDown(event);
androidKeyProcessor.onKeyDown(event);
return super.onKeyDown(keyCode, event);
}

Expand Down