blob: 3e30b9b8c7777cf1cdc24a8f2cc70dacdc3da392 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.autofillservice.cts;
import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
import static android.autofillservice.cts.FragmentContainerActivity.FRAGMENT_TAG;
import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
import static android.autofillservice.cts.Helper.eventually;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
import static com.google.common.truth.Truth.assertThat;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
import android.util.Log;
import android.view.autofill.AutofillValue;
import android.widget.EditText;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class MultipleFragmentLoginTest extends AutoFillServiceTestCase {
private static final String LOG_TAG = MultipleFragmentLoginTest.class.getSimpleName();
@Rule
public final AutofillActivityTestRule<FragmentContainerActivity> mActivityRule =
new AutofillActivityTestRule<>(FragmentContainerActivity.class);
private FragmentContainerActivity mActivity;
private EditText mEditText1;
private EditText mEditText2;
@Before
public void init() {
mActivity = mActivityRule.getActivity();
mEditText1 = mActivity.findViewById(R.id.editText1);
mEditText2 = mActivity.findViewById(R.id.editText2);
}
@Test
public void loginOnTwoFragments() throws Exception {
enableService();
Bundle clientState = new Bundle();
clientState.putString("key", "value1");
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedFillResponse.CannedDataset.Builder()
.setField("editText1", "editText1-autofilled")
.setPresentation(createPresentation("dataset1"))
.build())
.setExtras(clientState)
.build());
final InstrumentedAutoFillService.FillRequest[] fillRequest =
new InstrumentedAutoFillService.FillRequest[1];
// Trigger autofill
eventually(() -> {
mActivity.syncRunOnUiThread(() -> {
mEditText2.requestFocus();
mEditText1.requestFocus();
});
try {
fillRequest[0] = sReplier.getNextFillRequest();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, (int) (FILL_TIMEOUT_MS * 2));
assertThat(fillRequest[0].data).isNull();
AssistStructure structure = fillRequest[0].contexts.get(0).getStructure();
assertThat(fillRequest[0].contexts.size()).isEqualTo(1);
assertThat(findNodeByResourceId(structure, "editText1")).isNotNull();
assertThat(findNodeByResourceId(structure, "editText2")).isNotNull();
assertThat(findNodeByResourceId(structure, "editText3")).isNull();
assertThat(findNodeByResourceId(structure, "editText4")).isNull();
assertThat(findNodeByResourceId(structure, "editText5")).isNull();
// Wait until autofill has been applied
sUiBot.selectDataset("dataset1");
sUiBot.assertShownByText("editText1-autofilled");
// Manually fill view
mActivity.syncRunOnUiThread(() -> mEditText2.setText("editText2-manually-filled"));
// Replacing the fragment focused a previously unknown view which triggers a new
// partition
clientState.putString("key", "value2");
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedFillResponse.CannedDataset.Builder()
.setField("editText3", "editText3-autofilled")
.setField("editText4", "editText4-autofilled")
.setPresentation(createPresentation("dataset2"))
.build())
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText2", "editText5")
.setExtras(clientState)
.build());
Log.i(LOG_TAG, "Switching Fragments");
mActivity.syncRunOnUiThread(
() -> mActivity.getFragmentManager().beginTransaction().replace(
R.id.rootContainer, new FragmentWithMoreEditTexts(),
FRAGMENT_TAG).commitNow());
EditText editText5 = mActivity.findViewById(R.id.editText5);
fillRequest[0] = sReplier.getNextFillRequest();
// The fillRequest should have a fillContext for each partition. The first partition
// should be filled in
assertThat(fillRequest[0].contexts.size()).isEqualTo(2);
assertThat(fillRequest[0].data.getString("key")).isEqualTo("value1");
AssistStructure structure1 = fillRequest[0].contexts.get(0).getStructure();
ViewNode editText1Node = findNodeByResourceId(structure1, "editText1");
// The actual value in the structure is not updated in FillRequest-contexts, but the
// autofill value is. For text views in SaveRequest both are updated, but this is the
// only exception.
assertThat(editText1Node.getAutofillValue()).isEqualTo(
AutofillValue.forText("editText1-autofilled"));
ViewNode editText2Node = findNodeByResourceId(structure1, "editText2");
// Manually filled fields are not send to onFill. They appear in onSave if they are set
// as saveable fields.
assertThat(editText2Node.getText().toString()).isEqualTo("");
assertThat(findNodeByResourceId(structure1, "editText3")).isNull();
assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
AssistStructure structure2 = fillRequest[0].contexts.get(1).getStructure();
assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
assertThat(findNodeByResourceId(structure2, "editText3")).isNotNull();
assertThat(findNodeByResourceId(structure2, "editText4")).isNotNull();
assertThat(findNodeByResourceId(structure2, "editText5")).isNotNull();
// Wait until autofill has been applied
sUiBot.selectDataset("dataset2");
sUiBot.assertShownByText("editText3-autofilled");
sUiBot.assertShownByText("editText4-autofilled");
// Manually fill view
mActivity.syncRunOnUiThread(() -> editText5.setText("editText5-manually-filled"));
// Finish activity and save data
mActivity.finish();
sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
// The saveRequest should have a fillContext for each partition with all the data
InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
assertThat(saveRequest.contexts.size()).isEqualTo(2);
assertThat(saveRequest.data.getString("key")).isEqualTo("value2");
structure1 = saveRequest.contexts.get(0).getStructure();
editText1Node = findNodeByResourceId(structure1, "editText1");
assertThat(editText1Node.getText().toString()).isEqualTo("editText1-autofilled");
editText2Node = findNodeByResourceId(structure1, "editText2");
assertThat(editText2Node.getText().toString()).isEqualTo("editText2-manually-filled");
assertThat(findNodeByResourceId(structure1, "editText3")).isNull();
assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
structure2 = saveRequest.contexts.get(1).getStructure();
assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
ViewNode editText3Node = findNodeByResourceId(structure2, "editText3");
assertThat(editText3Node.getText().toString()).isEqualTo("editText3-autofilled");
ViewNode editText4Node = findNodeByResourceId(structure2, "editText4");
assertThat(editText4Node.getText().toString()).isEqualTo("editText4-autofilled");
ViewNode editText5Node = findNodeByResourceId(structure2, "editText5");
assertThat(editText5Node.getText().toString()).isEqualTo("editText5-manually-filled");
}
@Test
public void uiDismissedWhenNonSavableFragmentIsGone() throws Exception {
uiDismissedWhenFragmentIsGoneText(false);
}
@Test
public void uiDismissedWhenSavableFragmentIsGone() throws Exception {
uiDismissedWhenFragmentIsGoneText(true);
}
private void uiDismissedWhenFragmentIsGoneText(boolean savable) throws Exception {
// Set service.
enableService();
// Set expectations.
final CannedFillResponse.Builder response = new CannedFillResponse.Builder()
.addDataset(new CannedFillResponse.CannedDataset.Builder()
.setField("editText1", "whatever")
.setPresentation(createPresentation("dataset1"))
.build());
if (savable) {
response.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText2");
}
sReplier.addResponse(response.build());
// Trigger autofill
mActivity.syncRunOnUiThread(() -> {
mEditText2.requestFocus();
mEditText1.requestFocus();
});
// Check UI is shown, but don't select it.
sReplier.getNextFillRequest();
sUiBot.assertDatasets("dataset1");
// Switch fragments
sReplier.addResponse(NO_RESPONSE);
mActivity.syncRunOnUiThread(
() -> mActivity.getFragmentManager().beginTransaction().replace(
R.id.rootContainer, new FragmentWithMoreEditTexts(),
FRAGMENT_TAG).commitNow());
// Make sure UI is gone.
sReplier.getNextFillRequest();
sUiBot.assertNoDatasets();
}
// TODO: add similar tests for fragment with virtual view
}