diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 444d0360cf20e..7b1ca47ffa0e8 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -43,7 +43,7 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch @NonNull private final TextInputChannel textInputChannel; @NonNull private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); @Nullable private TextInputChannel.Configuration configuration; - @Nullable private SparseArray mAutofillConfigurations; + @Nullable private SparseArray autofillConfiguration; @NonNull private ListenableEditingState mEditable; private boolean mRestartInputPending; @Nullable private InputConnection lastInputConnection; @@ -556,6 +556,7 @@ void clearTextInputClient() { } mEditable.removeEditingStateListener(this); notifyViewExited(); + configuration = null; updateAutofillConfigurationIfNeeded(null); inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); unlockPlatformViewInputConnection(); @@ -679,7 +680,7 @@ public void didChangeEditingState( // have changed. However if the value of an unfocused EditableText is changed in the framework, // such change will not be sent to the text input plugin until the next TextInput.attach call. private boolean needsAutofill() { - return mAutofillConfigurations != null; + return autofillConfiguration != null; } private void notifyViewEntered() { @@ -724,21 +725,20 @@ private void updateAutofillConfigurationIfNeeded(TextInputChannel.Configuration if (configuration == null || configuration.autofill == null) { // Disables autofill if the configuration doesn't have an autofill field. - mAutofillConfigurations = null; + autofillConfiguration = null; return; } final TextInputChannel.Configuration[] configurations = configuration.fields; - mAutofillConfigurations = new SparseArray<>(); + autofillConfiguration = new SparseArray<>(); if (configurations == null) { - mAutofillConfigurations.put( - configuration.autofill.uniqueIdentifier.hashCode(), configuration); + autofillConfiguration.put(configuration.autofill.uniqueIdentifier.hashCode(), configuration); } else { for (TextInputChannel.Configuration config : configurations) { TextInputChannel.Configuration.Autofill autofill = config.autofill; if (autofill != null) { - mAutofillConfigurations.put(autofill.uniqueIdentifier.hashCode(), config); + autofillConfiguration.put(autofill.uniqueIdentifier.hashCode(), config); afm.notifyValueChanged( mView, autofill.uniqueIdentifier.hashCode(), @@ -755,9 +755,9 @@ public void onProvideAutofillVirtualStructure(@NonNull ViewStructure structure, final String triggerIdentifier = configuration.autofill.uniqueIdentifier; final AutofillId parentId = structure.getAutofillId(); - for (int i = 0; i < mAutofillConfigurations.size(); i++) { - final int autofillId = mAutofillConfigurations.keyAt(i); - final TextInputChannel.Configuration config = mAutofillConfigurations.valueAt(i); + for (int i = 0; i < autofillConfiguration.size(); i++) { + final int autofillId = autofillConfiguration.keyAt(i); + final TextInputChannel.Configuration config = autofillConfiguration.valueAt(i); final TextInputChannel.Configuration.Autofill autofill = config.autofill; if (autofill == null) { continue; @@ -801,16 +801,16 @@ public void autofill(@NonNull SparseArray values) { return; } - final TextInputChannel.Configuration.Autofill currentAutofill = configuration.autofill; - if (currentAutofill == null) { + if (configuration == null || autofillConfiguration == null || configuration.autofill == null) { return; } + final TextInputChannel.Configuration.Autofill currentAutofill = configuration.autofill; final HashMap editingValues = new HashMap<>(); for (int i = 0; i < values.size(); i++) { int virtualId = values.keyAt(i); - final TextInputChannel.Configuration config = mAutofillConfigurations.get(virtualId); + final TextInputChannel.Configuration config = autofillConfiguration.get(virtualId); if (config == null || config.autofill == null) { continue; } 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 0fde5814ab64c..ab47a9978d311 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -1781,6 +1781,53 @@ public void autofill_testAutofillUpdatesTheFramework() { assertEquals(editState.text, "unfocused field"); } + @Test + public void autofill_doesNotCrashAfterClearClientCall() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + FlutterView testView = new FlutterView(ctx); + TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class))); + TextInputPlugin textInputPlugin = + new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); + // Set up an autofill scenario with 2 fields. + final TextInputChannel.Configuration.Autofill autofillConfig = + new TextInputChannel.Configuration.Autofill( + "1", + new String[] {"HINT1"}, + "placeholder1", + new TextInputChannel.TextEditState("", 0, 0, -1, -1)); + final TextInputChannel.Configuration config = + new TextInputChannel.Configuration( + false, + false, + true, + true, + false, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + autofillConfig, + null); + + textInputPlugin.setTextInputClient(0, config); + textInputPlugin.setTextInputEditingState( + testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1)); + textInputPlugin.clearTextInputClient(); + + final SparseArray autofillValues = new SparseArray(); + autofillValues.append("1".hashCode(), AutofillValue.forText("focused field")); + autofillValues.append("2".hashCode(), AutofillValue.forText("unfocused field")); + + // Autofill both fields. + textInputPlugin.autofill(autofillValues); + + verify(textInputChannel, never()).updateEditingStateWithTag(anyInt(), any()); + verify(textInputChannel, never()) + .updateEditingState(anyInt(), any(), anyInt(), anyInt(), anyInt(), anyInt()); + } + @Test public void autofill_testSetTextIpnutClientUpdatesSideFields() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {