Add test cases for hints config
Bug: 219844915
Test: atest android.autofillservice.cts.dialog.LoginActivityTest
Change-Id: Ia6aacd933f0f3d7ee5f49e7fa98223e390b219a6
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 28d4ebd..bbce105 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -145,6 +145,7 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".activities.FieldsNoPasswordActivity"/>
<activity android:name=".activities.AugmentedAuthActivity" />
<activity android:name=".activities.SimpleAfterLoginActivity"/>
<activity android:name=".activities.SimpleBeforeLoginActivity"/>
diff --git a/tests/autofillservice/res/layout/multiple_hints_without_password_activity.xml b/tests/autofillservice/res/layout/multiple_hints_without_password_activity.xml
new file mode 100644
index 0000000..620c6e7
--- /dev/null
+++ b/tests/autofillservice/res/layout/multiple_hints_without_password_activity.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="username" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/cc_number_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="CC Number" />
+
+ <EditText
+ android:id="@+id/cc_number"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="creditCardNumber" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/email_address_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Email Address" />
+
+ <EditText
+ android:id="@+id/email_address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="emailAddress" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/phone_number_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Phone Number" />
+
+ <EditText
+ android:id="@+id/phone_number"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="phone" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/FieldsNoPasswordActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/FieldsNoPasswordActivity.java
new file mode 100644
index 0000000..fad2808
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/FieldsNoPasswordActivity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.activities;
+
+import android.autofillservice.cts.R;
+import android.os.Bundle;
+import android.view.WindowInsets;
+import android.widget.EditText;
+
+
+/**
+ * The Activity that is the same as {@link LoginActivity} layout but without password field.
+ */
+public class FieldsNoPasswordActivity extends AbstractAutoFillActivity {
+ public static final String STRING_ID_PHONE = "phone_number";
+ public static final String STRING_ID_CREDIT_CARD_NUMBER = "cc_number";
+ public static final String STRING_ID_EMAILADDRESS = "email_address";
+
+ private static FieldsNoPasswordActivity sCurrentActivity;
+
+ private EditText mUsernameEditText;
+
+ public static FieldsNoPasswordActivity getCurrentActivity() {
+ return sCurrentActivity;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getContentView());
+ sCurrentActivity = this;
+
+ mUsernameEditText = findViewById(R.id.username);
+ }
+
+ protected int getContentView() {
+ return R.layout.multiple_hints_without_password_activity;
+ }
+
+ public WindowInsets getRootWindowInsets() {
+ return mUsernameEditText.getRootWindowInsets();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/commontests/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/commontests/AutoFillServiceTestCase.java
index dca70f8..347ded0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/commontests/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/commontests/AutoFillServiceTestCase.java
@@ -16,6 +16,7 @@
package android.autofillservice.cts.commontests;
+import static android.autofillservice.cts.testcore.Helper.DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS;
import static android.autofillservice.cts.testcore.Helper.getContext;
import static android.autofillservice.cts.testcore.InstrumentedAutoFillService.SERVICE_NAME;
import static android.content.Context.CLIPBOARD_SERVICE;
@@ -298,6 +299,11 @@
AutofillManager.DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED,
Boolean.toString(false)))
//
+ // Hints list of Fill Dialog should be empty by default
+ .around(new DeviceConfigStateChangerRule(sContext, DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS,
+ ""))
+ //
// Finally, let subclasses add their own rules (like ActivityTestRule)
.around(getMainTestRule());
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
index ec8a4d1..6807600 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
@@ -16,21 +16,28 @@
package android.autofillservice.cts.dialog;
+import static android.autofillservice.cts.activities.FieldsNoPasswordActivity.STRING_ID_CREDIT_CARD_NUMBER;
+import static android.autofillservice.cts.activities.FieldsNoPasswordActivity.STRING_ID_EMAILADDRESS;
+import static android.autofillservice.cts.activities.FieldsNoPasswordActivity.STRING_ID_PHONE;
import static android.autofillservice.cts.testcore.Helper.ID_PASSWORD;
import static android.autofillservice.cts.testcore.Helper.ID_USERNAME;
+import static android.autofillservice.cts.testcore.Helper.ID_USERNAME_LABEL;
import static android.autofillservice.cts.testcore.Helper.assertHasFlags;
+import static android.autofillservice.cts.testcore.Helper.assertNoFlags;
import static android.autofillservice.cts.testcore.Helper.enableFillDialogFeature;
import static android.autofillservice.cts.testcore.Helper.isImeShowing;
+import static android.autofillservice.cts.testcore.Helper.setFillDialogHints;
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
-
import static com.google.common.truth.Truth.assertThat;
+import android.autofillservice.cts.activities.FieldsNoPasswordActivity;
import android.autofillservice.cts.activities.LoginActivity;
import android.autofillservice.cts.commontests.AutoFillServiceTestCase;
import android.autofillservice.cts.testcore.CannedFillResponse;
import android.autofillservice.cts.testcore.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.testcore.InstrumentedAutoFillService.FillRequest;
+import android.content.Intent;
import android.support.test.uiautomator.UiObject2;
import android.view.View;
@@ -330,4 +337,135 @@
activity.assertAutoFilled();
}
+
+ @Test
+ public void testHints_empty_notShowFillDialog() throws Exception {
+ testHintsNotMatch("");
+ }
+
+ @Test
+ public void testHints_notExisting_notShowFillDialog() throws Exception {
+ testHintsNotMatch("name:postalAddress:postalCode");
+ }
+
+ private void testHintsNotMatch(String hints) throws Exception {
+ // Set hints config, enable fill dialog and test service
+ setFillDialogHints(sContext, hints);
+ enableService();
+
+ // Start activity and autofill is not triggered
+ final FieldsNoPasswordActivity activity = startNoPasswordActivity();
+ mUiBot.waitForIdleSync();
+
+ sReplier.assertNoUnhandledFillRequests();
+ mUiBot.waitForIdleSync();
+
+ // Set response with a dataset
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(STRING_ID_PHONE, "0123456789")
+ .setField(STRING_ID_CREDIT_CARD_NUMBER, "1234567890")
+ .setField(STRING_ID_EMAILADDRESS, "dude@test")
+ .setPresentation(createPresentation("Dropdown Presentation"))
+ .setDialogPresentation(createPresentation("Dialog Presentation"))
+ .build())
+ .setDialogHeader(createPresentation("Dialog Header"))
+ .setDialogTriggerIds(ID_USERNAME);
+ sReplier.addResponse(builder.build());
+
+ // Click on username field to trigger fill dialog
+ mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME);
+ mUiBot.waitForIdleSync();
+
+ // Check onFillRequest is called now, and the fill dialog is not shown
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+ assertNoFlags(fillRequest.flags, FLAG_SUPPORTS_FILL_DIALOG);
+ mUiBot.assertNoFillDialog();
+
+ // Verify IME is not shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
+
+ // Verify dropdown UI is shown and works
+ mUiBot.selectDataset("Dropdown Presentation");
+ }
+
+ @Test
+ public void testHints_emptyButEnabled_showFillDialog() throws Exception {
+ testHintsNotMatchButFeatureEnabled("");
+ }
+
+ @Test
+ public void testHints_notExistingButEnabled_showFillDialog() throws Exception {
+ testHintsNotMatchButFeatureEnabled("name:postalAddress:postalCode");
+ }
+
+ // Tests the activity does not have allowed hints but fill dialog is enabled
+ private void testHintsNotMatchButFeatureEnabled(String hints) throws Exception {
+ // Enable fill dialog feature
+ enableFillDialogFeature(sContext);
+ // The test step is the same as if there is at least one match in the allowed
+ // list when the feature is enabled.
+ testHintsConfigMatchAtLeastOneField(hints);
+ }
+
+ @Test
+ public void testHints_username_showFillDialog() throws Exception {
+ testHintsConfigMatchAtLeastOneField("username");
+ }
+
+ @Test
+ public void testHints_emailAddress_showFillDialog() throws Exception {
+ testHintsConfigMatchAtLeastOneField("emailAddress");
+ }
+
+ @Test
+ public void testHints_usernameAndPhone_showFillDialog() throws Exception {
+ testHintsConfigMatchAtLeastOneField("username:phone");
+ }
+
+ private void testHintsConfigMatchAtLeastOneField(String hints) throws Exception {
+ // Set hints config, enable fill dialog and test service
+ setFillDialogHints(sContext, hints);
+ enableService();
+
+ // Set response with a dataset
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(STRING_ID_PHONE, "0123456789")
+ .setField(STRING_ID_CREDIT_CARD_NUMBER, "1234567890")
+ .setField(STRING_ID_EMAILADDRESS, "dude@test")
+ .setPresentation(createPresentation("Dropdown Presentation"))
+ .setDialogPresentation(createPresentation("Dialog Presentation"))
+ .build())
+ .setDialogHeader(createPresentation("Dialog Header"))
+ .setDialogTriggerIds(ID_USERNAME);
+ sReplier.addResponse(builder.build());
+
+ // Start activity and autofill
+ final FieldsNoPasswordActivity activity = startNoPasswordActivity();
+ mUiBot.waitForIdleSync();
+
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+ assertHasFlags(fillRequest.flags, FLAG_SUPPORTS_FILL_DIALOG);
+
+ // Click on password field to trigger fill dialog
+ mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME);
+ mUiBot.waitForIdleSync();
+
+ // Verify IME is not shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isFalse();
+
+ // Verify fill dialog is shown and works
+ mUiBot.selectFillDialogDataset("Dialog Presentation");
+ }
+
+ private FieldsNoPasswordActivity startNoPasswordActivity() throws Exception {
+ final Intent intent = new Intent(mContext, FieldsNoPasswordActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ mUiBot.assertShownByRelativeId(ID_USERNAME_LABEL);
+ return FieldsNoPasswordActivity.getCurrentActivity();
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
index 20d9370..672cfb1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
@@ -143,6 +143,8 @@
"SETTINGS_SHELL_CMD_TIMEOUT", OneTimeSettingsListener.DEFAULT_TIMEOUT_MS / 2, 2,
OneTimeSettingsListener.DEFAULT_TIMEOUT_MS);
+ public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS = "autofill_dialog_hints";
+
/**
* Helper interface used to filter nodes.
*
@@ -1340,6 +1342,11 @@
.that(actualFlags & expectedFlags).isEqualTo(expectedFlags);
}
+ public static void assertNoFlags(int actualFlags, int expectedFlags) {
+ assertWithMessage("Flags %s in %s", expectedFlags, actualFlags)
+ .that(actualFlags & expectedFlags).isEqualTo(0);
+ }
+
public static String callbackEventAsString(int event) {
switch (event) {
case AutofillCallback.EVENT_INPUT_HIDDEN:
@@ -1648,11 +1655,33 @@
/**
* Enable fill dialog feature
*/
- public static void enableFillDialogFeature(@NonNull Context context) {
+ public static void enableFillDialogFeature(@NonNull Context context) {
DeviceConfigStateManager deviceConfigStateManager =
new DeviceConfigStateManager(context, DeviceConfig.NAMESPACE_AUTOFILL,
AutofillManager.DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED);
- deviceConfigStateManager.set("true");
+ setDeviceConfig(deviceConfigStateManager, "true");
+ }
+
+ /**
+ * Set hints list for fill dialog
+ */
+ public static void setFillDialogHints(@NonNull Context context, @Nullable String hints) {
+ DeviceConfigStateManager deviceConfigStateManager =
+ new DeviceConfigStateManager(context, DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS);
+ setDeviceConfig(deviceConfigStateManager, hints);
+ }
+
+ public static void setDeviceConfig(@NonNull DeviceConfigStateManager deviceConfigStateManager,
+ @Nullable String value) {
+ final String previousValue = deviceConfigStateManager.get();
+ if (TextUtils.isEmpty(value) && TextUtils.isEmpty(previousValue)
+ || TextUtils.equals(previousValue, value)) {
+ Log.v(TAG, "No changed in config: " + deviceConfigStateManager);
+ return;
+ }
+
+ deviceConfigStateManager.set(value);
}
/**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
index 14b7f5a..b36f393 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java
@@ -1368,4 +1368,11 @@
private UiObject2 findFillDialogHeaderPicker() throws Exception {
return waitForObject(FILL_DIALOG_HEADER_SELECTOR, UI_DATASET_PICKER_TIMEOUT);
}
+
+ /**
+ * Asserts the fill dialog is not shown.
+ */
+ public void assertNoFillDialog() throws Exception {
+ assertNeverShown("Fill dialog", FILL_DIALOG_SELECTOR, DATASET_PICKER_NOT_SHOWN_NAPTIME_MS);
+ }
}