[Android] Reset IME state in TextInputPlugin.clearTextInputClient (flutter/engine#49829)

## Description

This PR calls Android API `InputMethodManager.restartInput` to reset IMEs internal states. Otherwise some IMEs (Gboard for instance) keep reacting based on the previous input configuration until a new configuration is set.

- On Android native, `restartInput` is called in several places, for instance in f219798774/android/widget/TextView.java (L2458).
- On Compose, https://github.com/flutter/flutter/issues/70546#issuecomment-1088345561 pointed out where it is called.
- On Flutter, it is called at some point but mainly when another `TextField` is focused (it is mainly called in `setTextInputEditingState`).

## Related Issue

Fixes https://github.com/flutter/flutter/issues/70546.

## Tests

Adds 1 test.
This commit is contained in:
Bruno Leroux
2024-01-18 07:38:34 +01:00
committed by GitHub
parent cd3aff45f8
commit d925ac1a53
2 changed files with 39 additions and 0 deletions

View File

@@ -569,6 +569,10 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
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);
}
private static class InputTarget {

View File

@@ -1128,6 +1128,41 @@ public class TextInputPluginTest {
assertEquals(1, testImm.getRestartCount(testView));
}
@Test
public void clearTextInputClient_alwaysRestartsImm() {
// 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,
null,
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 is always forced when calling clearTextInputClient().
textInputPlugin.clearTextInputClient();
assertEquals(2, testImm.getRestartCount(testView));
}
@Test
public void destroy_clearTextInputMethodHandler() {
View testView = new View(ctx);