Skip to content

Commit e31cb0d

Browse files
[Android text input] fix android autofill on focused text field (flutter#24463)
1 parent e9e738c commit e31cb0d

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ private void setPlatformViewTextInputClient(int platformViewId) {
418418
mRestartInputPending = false;
419419
}
420420

421+
// Called by the text input channel to update the text input plugin with the
422+
// latest TextEditState from the framework.
421423
@VisibleForTesting
422424
void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
423425
mLastKnownFrameworkTextEditingState = state;
@@ -591,7 +593,7 @@ public void didChangeEditingState(
591593
final int selectionEnd = mEditable.getSelectionEnd();
592594
final int composingStart = mEditable.getComposingStart();
593595
final int composingEnd = mEditable.getComposingEnd();
594-
// Framework needs to sent value first.
596+
// The framework needs to send value first.
595597
final boolean skipFrameworkUpdate =
596598
mLastKnownFrameworkTextEditingState == null
597599
|| (mEditable.toString().equals(mLastKnownFrameworkTextEditingState.text)
@@ -773,11 +775,14 @@ public void autofill(SparseArray<AutofillValue> values) {
773775
final TextInputChannel.TextEditState newState =
774776
new TextInputChannel.TextEditState(value, value.length(), value.length(), -1, -1);
775777

776-
// The value of the currently focused text field needs to be updated.
777778
if (autofill.uniqueIdentifier.equals(currentAutofill.uniqueIdentifier)) {
778-
setTextInputEditingState(mView, newState);
779+
// Autofilling the current client is the same as handling user input
780+
// from the virtual keyboard. Setting the editable to newState and an
781+
// update will be sent to the framework.
782+
mEditable.setEditingState(newState);
783+
} else {
784+
editingValues.put(autofill.uniqueIdentifier, newState);
779785
}
780-
editingValues.put(autofill.uniqueIdentifier, newState);
781786
}
782787

783788
textInputChannel.updateEditingStateWithTag(inputTarget.id, editingValues);

shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java

+88
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.provider.Settings;
2828
import android.text.InputType;
2929
import android.text.Selection;
30+
import android.util.SparseArray;
3031
import android.util.SparseIntArray;
3132
import android.view.KeyEvent;
3233
import android.view.View;
@@ -55,6 +56,7 @@
5556
import io.flutter.plugin.platform.PlatformViewsController;
5657
import java.nio.ByteBuffer;
5758
import java.util.ArrayList;
59+
import java.util.HashMap;
5860
import java.util.List;
5961
import org.json.JSONArray;
6062
import org.json.JSONException;
@@ -927,6 +929,92 @@ public void autofill_testLifeCycle() {
927929
assertEquals("1".hashCode(), testAfm.exitId);
928930
}
929931

932+
@Test
933+
public void autofill_testAutofillUpdatesTheFramework() {
934+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
935+
return;
936+
}
937+
938+
TestAfm testAfm =
939+
Shadow.extract(RuntimeEnvironment.application.getSystemService(AutofillManager.class));
940+
FlutterView testView = new FlutterView(RuntimeEnvironment.application);
941+
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
942+
TextInputPlugin textInputPlugin =
943+
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
944+
945+
// Set up an autofill scenario with 2 fields.
946+
final TextInputChannel.Configuration.Autofill autofill1 =
947+
new TextInputChannel.Configuration.Autofill(
948+
"1",
949+
new String[] {"HINT1"},
950+
new TextInputChannel.TextEditState("field 1", 0, 0, -1, -1));
951+
final TextInputChannel.Configuration.Autofill autofill2 =
952+
new TextInputChannel.Configuration.Autofill(
953+
"2",
954+
new String[] {"HINT2", "EXTRA"},
955+
new TextInputChannel.TextEditState("field 2", 0, 0, -1, -1));
956+
957+
final TextInputChannel.Configuration config1 =
958+
new TextInputChannel.Configuration(
959+
false,
960+
false,
961+
true,
962+
TextInputChannel.TextCapitalization.NONE,
963+
null,
964+
null,
965+
null,
966+
autofill1,
967+
null);
968+
final TextInputChannel.Configuration config2 =
969+
new TextInputChannel.Configuration(
970+
false,
971+
false,
972+
true,
973+
TextInputChannel.TextCapitalization.NONE,
974+
null,
975+
null,
976+
null,
977+
autofill2,
978+
null);
979+
980+
final TextInputChannel.Configuration autofillConfiguration =
981+
new TextInputChannel.Configuration(
982+
false,
983+
false,
984+
true,
985+
TextInputChannel.TextCapitalization.NONE,
986+
null,
987+
null,
988+
null,
989+
autofill1,
990+
new TextInputChannel.Configuration[] {config1, config2});
991+
992+
textInputPlugin.setTextInputClient(0, autofillConfiguration);
993+
textInputPlugin.setTextInputEditingState(
994+
testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1));
995+
996+
final SparseArray<AutofillValue> autofillValues = new SparseArray();
997+
autofillValues.append("1".hashCode(), AutofillValue.forText("focused field"));
998+
autofillValues.append("2".hashCode(), AutofillValue.forText("unfocused field"));
999+
1000+
// Autofill both fields.
1001+
textInputPlugin.autofill(autofillValues);
1002+
1003+
// Verify the Editable has been updated.
1004+
assertTrue(textInputPlugin.getEditable().toString().equals("focused field"));
1005+
1006+
// The autofill value of the focused field is sent via updateEditingState.
1007+
verify(textInputChannel, times(1))
1008+
.updateEditingState(anyInt(), eq("focused field"), eq(13), eq(13), eq(-1), eq(-1));
1009+
1010+
final ArgumentCaptor<HashMap> mapCaptor = ArgumentCaptor.forClass(HashMap.class);
1011+
1012+
verify(textInputChannel, times(1)).updateEditingStateWithTag(anyInt(), mapCaptor.capture());
1013+
final TextInputChannel.TextEditState editState =
1014+
(TextInputChannel.TextEditState) mapCaptor.getValue().get("2");
1015+
assertEquals(editState.text, "unfocused field");
1016+
}
1017+
9301018
@Test
9311019
public void autofill_testSetTextIpnutClientUpdatesSideFields() {
9321020
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {

0 commit comments

Comments
 (0)