-
Notifications
You must be signed in to change notification settings - Fork 6k
Handle backspace on Android by using the engine's ICU grapheme breaker #18041
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| #include <android/native_window_jni.h> | ||
|
|
||
| #include <utility> | ||
| #include "unicode/brkiter.h" | ||
|
|
||
| #include "flutter/assets/directory_asset_bundle.h" | ||
| #include "flutter/common/settings.h" | ||
|
|
@@ -484,6 +485,28 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, | |
| ); | ||
| } | ||
|
|
||
| // Return the index of the first grapheme cluster that precedes the given | ||
| // offset in the text. | ||
| static jint GetTextOffsetBefore(JNIEnv* env, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a tiny docstring for this function?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| jclass jcaller, | ||
| jstring text, | ||
| jint offset) { | ||
| std::unique_ptr<icu::BreakIterator> breaker; | ||
| UErrorCode status = U_ZERO_ERROR; | ||
| breaker.reset( | ||
| icu::BreakIterator::createCharacterInstance(icu::Locale(), status)); | ||
| if (!U_SUCCESS(status)) { | ||
| fml::jni::ThrowException(env, "java/lang/IllegalStateException", | ||
| "unable to create character iterator"); | ||
| return -1; | ||
| } | ||
|
|
||
| fml::jni::ScopedJavaStringChars chars(env, text); | ||
| breaker->setText( | ||
| icu::UnicodeString(false, chars.chars(), env->GetStringLength(text))); | ||
| return breaker->preceding(offset); | ||
| } | ||
|
|
||
| bool RegisterApi(JNIEnv* env) { | ||
| static const JNINativeMethod flutter_jni_methods[] = { | ||
| // Start of methods from FlutterJNI | ||
|
|
@@ -599,6 +622,12 @@ bool RegisterApi(JNIEnv* env) { | |
| .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", | ||
| .fnPtr = reinterpret_cast<void*>(&LookupCallbackInformation), | ||
| }, | ||
|
|
||
| { | ||
| .name = "nativeGetTextOffsetBefore", | ||
| .signature = "(Ljava/lang/String;I)I", | ||
| .fnPtr = reinterpret_cast<void*>(&GetTextOffsetBefore), | ||
| }, | ||
| }; | ||
|
|
||
| if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| import android.text.InputType; | ||
| import android.text.Selection; | ||
| import android.text.SpannableStringBuilder; | ||
| import android.text.TextUtils; | ||
| import android.view.KeyEvent; | ||
| import android.view.View; | ||
| import android.view.inputmethod.EditorInfo; | ||
|
|
@@ -318,6 +319,13 @@ public void testSendKeyEvent_delKeyDeletesBackward() { | |
| int selStart = 29; | ||
| Editable editable = sampleRtlEditable(selStart, selStart); | ||
| InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable); | ||
| adaptor.setGetOffsetBefore( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to just adjust an old test, where is the test that exercises the new logic?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a reimplementation of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've looked into this in the past, let me try to remember. There are 2 ways you can make unit tests, robolectric or a slower test that runs on an emulator. Robolectric can load JNI but your JNI library has to be compiled for the desktop operating system, which might be easy or near impossible depending on what you are doing. It's near impossible if you are calling native android libraries. |
||
| new InputConnectionAdaptor.GetOffsetBefore() { | ||
| @Override | ||
| public int getOffsetBefore(String text, int offset) { | ||
| return TextUtils.getOffsetBefore(text, offset); | ||
| } | ||
| }); | ||
|
|
||
| KeyEvent downKeyDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Don't we have access to lambdas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK the Android embedding does not use lambdas. The embedding supports Android versions back to API 16 and tries to avoid anything that would make it more difficult to support legacy toolchains and runtimes.