[Android] Call restartInput selectively on clear (flutter/engine#53662)

## Description

This PR restricts the call to `restartInput` which was added in https://github.com/flutter/engine/pull/49829.
The restart is called when input action is null, DONE, or NONE.

## Related Issue

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

## Tests

Updates 1 test, adds 3 tests.
This commit is contained in:
Bruno Leroux 2024-07-18 22:27:33 +02:00 committed by GitHub
parent 21df44f8b0
commit 42ec2e55c5
2 changed files with 120 additions and 4 deletions

View File

@ -546,15 +546,22 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
}
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 {

View File

@ -1131,7 +1131,7 @@ public class TextInputPluginTest {
}
@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 class TextInputPluginTest {
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);