From 2c0eee43923fb1ee140d9a55549965e0daf748ca Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 27 Nov 2019 07:51:04 +0900 Subject: [PATCH] Always set mEditable values when different in TextPlugin (#13951) --- .../plugin/editing/TextInputPlugin.java | 18 +++++++++++---- .../plugin/editing/TextInputPluginTest.java | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 476592669..e66e27053 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -102,6 +102,10 @@ public class TextInputPlugin { return mImm; } + @VisibleForTesting Editable getEditable() { + return mEditable; + } + /*** * Use the current platform view input connection until unlockPlatformViewInputConnection is called. * @@ -306,15 +310,21 @@ public class TextInputPlugin { } @VisibleForTesting void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { - if (!restartAlwaysRequired && !mRestartInputPending && state.text.equals(mEditable.toString())) { - applyStateToSelection(state); + // Always replace the contents of mEditable if the text differs + if (!state.text.equals(mEditable.toString())) { + mEditable.replace(0, mEditable.length(), state.text); + } + // Always apply state to selection which handles updating the selection if needed. + applyStateToSelection(state); + // Use updateSelection to update imm on selection if it is not neccessary to restart. + if (!restartAlwaysRequired && !mRestartInputPending) { mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0), Math.max(Selection.getSelectionEnd(mEditable), 0), BaseInputConnection.getComposingSpanStart(mEditable), BaseInputConnection.getComposingSpanEnd(mEditable)); + // Restart if there is a pending restart or the device requires a force restart + // (see isRestartAlwaysRequired). Restarting will also update the selection. } else { - mEditable.replace(0, mEditable.length(), state.text); - applyStateToSelection(state); mImm.restartInput(view); mRestartInputPending = false; } diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index 96bf9e39d..e4cd753b8 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -29,6 +29,7 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.platform.PlatformViewsController; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -73,6 +74,27 @@ public class TextInputPluginTest { assertEquals(1, testImm.getRestartCount(testView)); } + @Test + public void setTextInputEditingState_alwaysSetEditableWhenDifferent() { + // Initialize a general TextInputPlugin. + InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); + TestImm testImm = Shadow.extract(RuntimeEnvironment.application.getSystemService(Context.INPUT_METHOD_SERVICE)); + testImm.setCurrentInputMethodSubtype(inputMethodSubtype); + View testView = new View(RuntimeEnvironment.application); + TextInputPlugin textInputPlugin = new TextInputPlugin(testView, mock(DartExecutor.class), mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient(0, new TextInputChannel.Configuration(false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + // There's a pending restart since we initialized the text input client. Flush that now. With changed text, we should + // always set the Editable contents. + textInputPlugin.setTextInputEditingState(testView, new TextInputChannel.TextEditState("hello", 0, 0)); + assertEquals(1, testImm.getRestartCount(testView)); + assertTrue(textInputPlugin.getEditable().toString().equals("hello")); + + // No pending restart, set Editable contents anyways. + textInputPlugin.setTextInputEditingState(testView, new TextInputChannel.TextEditState("Shibuyawoo", 0, 0)); + assertEquals(1, testImm.getRestartCount(testView)); + assertTrue(textInputPlugin.getEditable().toString().equals("Shibuyawoo")); + } + // See https://github.com/flutter/flutter/issues/29341 and https://github.com/flutter/flutter/issues/31512 // All modern Samsung keybords are affected including non-korean languages and thus // need the restart. -- GitLab