[Android] Fix TextInputType.none for devices with physical keyboard (flutter/engine#49980)
## Description This PR fixes an issue where keystrokes aren't received on Android devices with physical keyboards (e.g. rugged Zebra devices) when `keyboardType` is set to `TextInputType.none` on a `TextField`. The logic in `setTextInputClient` and `canShowTextInput` created an `inputTarget` with `InputTarget.Type.NO_TARGET` which caused the [input connection to short circuit](https://github.com/flutter/engine/blob/main/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java#L296) and not be established. Bug introduction PR: https://github.com/flutter/engine/pull/26585 ## Related Issue https://github.com/flutter/flutter/issues/89983 ## Unit Test Notes - The existing `showTextInput_textInputTypeNone()` stays green after update. - `inputConnection_textInputTypeNone()` updated to `assertNotNull`. I would make this more specific, but this is my first venture into the Flutter engine and don't know enough about those connection attributes. ## Demo Video below with Zebra MC9300 device. This issue can also be reproduced in a standard android emulator. Simply add a `TextField`, configure `keyboardType` to be `TextInputType.none` and attempt to enter text after running and giving focus to textfield. Before https://github.com/flutter/engine/assets/1988098/348ca061-b8b9-4483-956e-0732c1238207 After https://github.com/flutter/engine/assets/1988098/b65c7251-59b4-4c73-9b85-7ac03f47a7e4 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing.
This commit is contained in:
@@ -376,16 +376,11 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
|
||||
mImm.sendAppPrivateCommand(mView, action, data);
|
||||
}
|
||||
|
||||
private boolean canShowTextInput() {
|
||||
if (configuration == null || configuration.inputType == null) {
|
||||
return true;
|
||||
}
|
||||
return configuration.inputType.type != TextInputChannel.TextInputType.NONE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void showTextInput(View view) {
|
||||
if (canShowTextInput()) {
|
||||
if (configuration == null
|
||||
|| configuration.inputType == null
|
||||
|| configuration.inputType.type != TextInputChannel.TextInputType.NONE) {
|
||||
view.requestFocus();
|
||||
mImm.showSoftInput(view, 0);
|
||||
} else {
|
||||
@@ -409,11 +404,7 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
|
||||
// Call notifyViewExited on the previous field.
|
||||
notifyViewExited();
|
||||
this.configuration = configuration;
|
||||
if (canShowTextInput()) {
|
||||
inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client);
|
||||
} else {
|
||||
inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, client);
|
||||
}
|
||||
inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client);
|
||||
|
||||
mEditable.removeEditingStateListener(this);
|
||||
mEditable =
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.flutter.plugin.editing;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.AdditionalMatchers.aryEq;
|
||||
@@ -1176,8 +1177,8 @@ public class TextInputPluginTest {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
// DartExecutor.send is deprecated.
|
||||
@Test
|
||||
public void inputConnection_createsActionFromEnter() throws JSONException {
|
||||
private void verifyInputConnection(TextInputChannel.TextInputType textInputType)
|
||||
throws JSONException {
|
||||
TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE));
|
||||
FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
|
||||
View testView = new View(ctx);
|
||||
@@ -1194,7 +1195,7 @@ public class TextInputPluginTest {
|
||||
true,
|
||||
false,
|
||||
TextInputChannel.TextCapitalization.NONE,
|
||||
new TextInputChannel.InputType(TextInputChannel.TextInputType.TEXT, false, false),
|
||||
new TextInputChannel.InputType(textInputType, false, false),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -1232,6 +1233,16 @@ public class TextInputPluginTest {
|
||||
new String[] {"0", "TextInputAction.done"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputConnection_createsActionFromEnter() throws JSONException {
|
||||
verifyInputConnection(TextInputChannel.TextInputType.TEXT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputConnection_respondsToKeyEvents_textInputTypeNone() throws JSONException {
|
||||
verifyInputConnection(TextInputChannel.TextInputType.NONE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // InputMethodSubtype
|
||||
@Test
|
||||
public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException {
|
||||
@@ -1310,7 +1321,7 @@ public class TextInputPluginTest {
|
||||
InputConnection connection =
|
||||
textInputPlugin.createInputConnection(
|
||||
testView, mock(KeyboardManager.class), new EditorInfo());
|
||||
assertEquals(connection, null);
|
||||
assertNotNull(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user