diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index d6c3c6f90d483..a021214164ede 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -546,15 +546,22 @@ void clearTextInputClient() { } mEditable.removeEditingStateListener(this); notifyViewExited(); + + boolean needsRestart = + configuration.inputAction == null + || configuration.inputAction == EditorInfo.IME_ACTION_DONE + || configuration.inputAction == EditorInfo.IME_ACTION_NONE; configuration = null; updateAutofillConfigurationIfNeeded(null); inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); unlockPlatformViewInputConnection(); lastClientRect = null; - // Call restartInput to reset IME internal states. Otherwise some IMEs (Gboard for instance) - // keep reacting based on the previous input configuration until a new configuration is set. - mImm.restartInput(mView); + if (needsRestart) { + // Call restartInput to reset IME internal states. Otherwise some IMEs (Gboard for instance) + // keep reacting based on the previous input configuration until a new configuration is set. + mImm.restartInput(mView); + } } private static class InputTarget { 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 cc04befc45d9e..1c8b0517e818d 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -1131,7 +1131,7 @@ public void setTextInputEditingState_nullInputMethodSubtype() { } @Test - public void clearTextInputClient_alwaysRestartsImm() { + public void clearTextInputClient_restartsImmWhenInputActionIsNull() { // Initialize a general TextInputPlugin. InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE)); @@ -1165,6 +1165,115 @@ public void clearTextInputClient_alwaysRestartsImm() { assertEquals(2, testImm.getRestartCount(testView)); } + @Test + public void clearTextInputClient_restartsImmWhenInputActionIsDone() { + // Initialize a general TextInputPlugin. + InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); + TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE)); + testImm.setCurrentInputMethodSubtype(inputMethodSubtype); + View testView = new View(ctx); + TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class)); + TextInputPlugin textInputPlugin = + new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + true, + true, + false, + TextInputChannel.TextCapitalization.NONE, + null, + EditorInfo.IME_ACTION_DONE, + null, + null, + null, + null)); + // There's a pending restart since we initialized the text input client. Flush that now. + textInputPlugin.setTextInputEditingState( + testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1)); + assertEquals(1, testImm.getRestartCount(testView)); + + // A restart should be forced when calling clearTextInputClient() and input action is + // EditorInfo.IME_ACTION_DONE. + textInputPlugin.clearTextInputClient(); + assertEquals(2, testImm.getRestartCount(testView)); + } + + @Test + public void clearTextInputClient_restartsImmWhenInputActionIsNone() { + // Initialize a general TextInputPlugin. + InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); + TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE)); + testImm.setCurrentInputMethodSubtype(inputMethodSubtype); + View testView = new View(ctx); + TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class)); + TextInputPlugin textInputPlugin = + new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + true, + true, + false, + TextInputChannel.TextCapitalization.NONE, + null, + EditorInfo.IME_ACTION_NONE, + null, + null, + null, + null)); + // There's a pending restart since we initialized the text input client. Flush that now. + textInputPlugin.setTextInputEditingState( + testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1)); + assertEquals(1, testImm.getRestartCount(testView)); + + // A restart should be forced when calling clearTextInputClient() and input action is + // EditorInfo.IME_ACTION_NONE. + textInputPlugin.clearTextInputClient(); + assertEquals(2, testImm.getRestartCount(testView)); + } + + @Test + public void clearTextInputClient_doesNotRestartImmWhenInputActionIsNext() { + // Regression test for https://github.com/flutter/flutter/issues/148673. + // Initialize a general TextInputPlugin. + InputMethodSubtype inputMethodSubtype = mock(InputMethodSubtype.class); + TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE)); + testImm.setCurrentInputMethodSubtype(inputMethodSubtype); + View testView = new View(ctx); + TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class)); + TextInputPlugin textInputPlugin = + new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + true, + true, + false, + TextInputChannel.TextCapitalization.NONE, + null, + EditorInfo.IME_ACTION_NEXT, + null, + null, + null, + null)); + // There's a pending restart since we initialized the text input client. Flush that now. + textInputPlugin.setTextInputEditingState( + testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1)); + assertEquals(1, testImm.getRestartCount(testView)); + + // No restart is forced when calling clearTextInputClient() and input action is + // EditorInfo.IME_ACTION_NEXT. + textInputPlugin.clearTextInputClient(); + assertEquals(1, testImm.getRestartCount(testView)); + } + @Test public void destroy_clearTextInputMethodHandler() { View testView = new View(ctx);