Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ private void setPlatformViewTextInputClient(int platformViewId) {
mRestartInputPending = false;
}

// Called by the text input channel to update the text input plugin with the
// latest TextEditState from the framework.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: TextEditState => TextEditingState

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class that this method takes as a parameter is called TextEditState.

@VisibleForTesting
void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
mLastKnownFrameworkTextEditingState = state;
Expand Down Expand Up @@ -591,7 +593,7 @@ public void didChangeEditingState(
final int selectionEnd = mEditable.getSelectionEnd();
final int composingStart = mEditable.getComposingStart();
final int composingEnd = mEditable.getComposingEnd();
// Framework needs to sent value first.
// The framework needs to send value first.
final boolean skipFrameworkUpdate =
mLastKnownFrameworkTextEditingState == null
|| (mEditable.toString().equals(mLastKnownFrameworkTextEditingState.text)
Expand Down Expand Up @@ -773,11 +775,14 @@ public void autofill(SparseArray<AutofillValue> values) {
final TextInputChannel.TextEditState newState =
new TextInputChannel.TextEditState(value, value.length(), value.length(), -1, -1);

// The value of the currently focused text field needs to be updated.
if (autofill.uniqueIdentifier.equals(currentAutofill.uniqueIdentifier)) {
setTextInputEditingState(mView, newState);
// Autofilling the current client is the same as handling user input
// from the virtual keyboard. Setting the editable to newState and an
// update will be sent to the framework.
mEditable.setEditingState(newState);
} else {
editingValues.put(autofill.uniqueIdentifier, newState);
}
editingValues.put(autofill.uniqueIdentifier, newState);
}

textInputChannel.updateEditingStateWithTag(inputTarget.id, editingValues);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import android.provider.Settings;
import android.text.InputType;
import android.text.Selection;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.View;
Expand Down Expand Up @@ -55,6 +56,7 @@
import io.flutter.plugin.platform.PlatformViewsController;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -927,6 +929,92 @@ public void autofill_testLifeCycle() {
assertEquals("1".hashCode(), testAfm.exitId);
}

@Test
public void autofill_testAutofillUpdatesTheFramework() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}

TestAfm testAfm =
Shadow.extract(RuntimeEnvironment.application.getSystemService(AutofillManager.class));
FlutterView testView = new FlutterView(RuntimeEnvironment.application);
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 autofill1 =
new TextInputChannel.Configuration.Autofill(
"1",
new String[] {"HINT1"},
new TextInputChannel.TextEditState("field 1", 0, 0, -1, -1));
final TextInputChannel.Configuration.Autofill autofill2 =
new TextInputChannel.Configuration.Autofill(
"2",
new String[] {"HINT2", "EXTRA"},
new TextInputChannel.TextEditState("field 2", 0, 0, -1, -1));

final TextInputChannel.Configuration config1 =
new TextInputChannel.Configuration(
false,
false,
true,
TextInputChannel.TextCapitalization.NONE,
null,
null,
null,
autofill1,
null);
final TextInputChannel.Configuration config2 =
new TextInputChannel.Configuration(
false,
false,
true,
TextInputChannel.TextCapitalization.NONE,
null,
null,
null,
autofill2,
null);

final TextInputChannel.Configuration autofillConfiguration =
new TextInputChannel.Configuration(
false,
false,
true,
TextInputChannel.TextCapitalization.NONE,
null,
null,
null,
autofill1,
new TextInputChannel.Configuration[] {config1, config2});

textInputPlugin.setTextInputClient(0, autofillConfiguration);
textInputPlugin.setTextInputEditingState(
testView, new TextInputChannel.TextEditState("", 0, 0, -1, -1));

final SparseArray<AutofillValue> 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 the Editable has been updated.
assertTrue(textInputPlugin.getEditable().toString().equals("focused field"));

// The autofill value of the focused field is sent via updateEditingState.
verify(textInputChannel, times(1))
.updateEditingState(anyInt(), eq("focused field"), eq(13), eq(13), eq(-1), eq(-1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do the 13s come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"focused field".length == 13, the autofill implementation puts the caret at the end.


final ArgumentCaptor<HashMap> mapCaptor = ArgumentCaptor.forClass(HashMap.class);

verify(textInputChannel, times(1)).updateEditingStateWithTag(anyInt(), mapCaptor.capture());
final TextInputChannel.TextEditState editState =
(TextInputChannel.TextEditState) mapCaptor.getValue().get("2");
assertEquals(editState.text, "unfocused field");
}

@Test
public void autofill_testSetTextIpnutClientUpdatesSideFields() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Expand Down