Update text when Active Unlock is enabled.
Update the security summary, intro, and unlock your phone summary when
Active Unlock is enabled and enrolled on the device.
Test: make RunSettingsRoboTests
Test: manually flip flags, confirm combined page has updated strings
Bug: 264812018
Change-Id: I2843e9f3aa0f38a9f2ebb18d60fed6293f9ce36e
diff --git a/res/xml/security_settings_combined_biometric.xml b/res/xml/security_settings_combined_biometric.xml
index 4476281..1bbe7b3 100644
--- a/res/xml/security_settings_combined_biometric.xml
+++ b/res/xml/security_settings_combined_biometric.xml
@@ -20,6 +20,7 @@
android:title="@string/security_settings_biometric_preference_title">
<com.android.settingslib.widget.TopIntroPreference
+ android:key="biometric_intro"
android:title="@string/biometric_settings_intro" />
<PreferenceCategory
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
new file mode 100644
index 0000000..1badb0f
--- /dev/null
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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 com.android.settings.biometrics.activeunlock;
+
+import android.content.Context;
+
+/** Listens to device name updates from the content provider and fetches the latest value. */
+public class ActiveUnlockDeviceNameListener {
+ private static final String TAG = "ActiveUnlockDeviceNameListener";
+ private static final String METHOD_NAME = "getDeviceName";
+ private static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
+
+ private final ActiveUnlockContentListener mActiveUnlockContentListener;
+ public ActiveUnlockDeviceNameListener(
+ Context context, ActiveUnlockContentListener.OnContentChangedListener listener) {
+ mActiveUnlockContentListener = new ActiveUnlockContentListener(
+ context, listener, TAG, METHOD_NAME, DEVICE_NAME_KEY);
+ }
+
+ /** Returns whether a device is enrolled in Active Unlock. */
+ public boolean hasEnrolled() {
+ return mActiveUnlockContentListener.getContent() != null;
+ }
+
+ /** Subscribes to device name updates. */
+ public void subscribe() {
+ mActiveUnlockContentListener.subscribe();
+ }
+
+ /** Unsubscribes from device name updates. */
+ public void unsubscribe() {
+ mActiveUnlockContentListener.unsubscribe();
+ }
+}
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
index 640f08d..439f176 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -31,7 +31,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.BasePreferenceController.AvailabilityStatus;
@@ -164,6 +166,82 @@
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
}
+ /**
+ * Returns the title of the combined biometric settings entity when active unlock is enabled.
+ */
+ public String getTitleForActiveUnlock() {
+ final boolean faceAllowed = Utils.hasFaceHardware(mContext);
+ final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
+ return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
+ }
+
+ @StringRes
+ private static int getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
+ if (isFaceAllowed && isFingerprintAllowed) {
+ return R.string.security_settings_biometric_preference_title;
+ } else if (isFaceAllowed) {
+ return R.string.security_settings_face_preference_title;
+ } else if (isFingerprintAllowed) {
+ return R.string.security_settings_fingerprint_preference_title;
+ } else {
+ // Default to original summary, but this case should never happen.
+ return R.string.security_settings_biometric_preference_title;
+ }
+ }
+
+ /**
+ * Returns the intro of the combined biometric settings entity when active unlock is enabled.
+ */
+ public String getIntroForActiveUnlock() {
+ final boolean faceAllowed = Utils.hasFaceHardware(mContext);
+ final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
+ if (useBiometricFailureLayout()) {
+ int introRes = getIntroRes(faceAllowed, fingerprintAllowed);
+ return introRes == 0 ? "" : mContext.getString(introRes);
+ }
+ if (useUnlockIntentLayout() && (!faceAllowed || !fingerprintAllowed)) {
+ return "";
+ }
+ return mContext.getString(R.string.biometric_settings_intro);
+ }
+
+ @StringRes
+ private static int getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
+ if (isFaceAllowed && isFingerprintAllowed) {
+ return R.string.biometric_settings_intro_with_activeunlock;
+ } else if (isFaceAllowed) {
+ return R.string.biometric_settings_intro_with_face;
+ } else if (isFingerprintAllowed) {
+ return R.string.biometric_settings_intro_with_fingerprint;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the summary of the unlock device entity when active unlock is enabled.
+ */
+ public String getUnlockDeviceSummaryForActiveUnlock() {
+ final boolean faceAllowed = Utils.hasFaceHardware(mContext);
+ final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
+
+ return mContext.getString(getUnlockDeviceSummaryRes(faceAllowed, fingerprintAllowed));
+ }
+
+ @StringRes
+ private static int getUnlockDeviceSummaryRes(
+ boolean isFaceAllowed, boolean isFingerprintAllowed) {
+ if (isFaceAllowed && isFingerprintAllowed) {
+ return R.string.biometric_settings_use_face_fingerprint_or_watch_preference_summary;
+ } else if (isFaceAllowed) {
+ return R.string.biometric_settings_use_face_or_watch_preference_summary;
+ } else if (isFingerprintAllowed) {
+ return R.string.biometric_settings_use_fingerprint_or_watch_preference_summary;
+ } else {
+ return R.string.biometric_settings_use_watch_preference_summary;
+ }
+ }
+
private static String getFlagState() {
return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
}
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 4da42d4..0a1d29d 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -107,10 +107,7 @@
launchChooseOrConfirmLock();
}
- final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
- if (unlockPhonePreference != null) {
- unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
- }
+ updateUnlockPhonePreferenceSummary();
final Preference useInAppsPreference = findPreference(getUseInAppsPreferenceKey());
if (useInAppsPreference != null) {
@@ -309,8 +306,15 @@
}
}
+ protected void updateUnlockPhonePreferenceSummary() {
+ final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
+ if (unlockPhonePreference != null) {
+ unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
+ }
+ }
+
@NonNull
- private String getUseAnyBiometricSummary() {
+ protected String getUseAnyBiometricSummary() {
boolean isFaceAllowed = mFaceManager != null && mFaceManager.isHardwareDetected();
boolean isFingerprintAllowed =
mFingerprintManager != null && mFingerprintManager.isHardwareDetected();
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
index 7e76ceb..a352e5a 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
@@ -17,8 +17,15 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockDeviceNameListener;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -32,6 +39,10 @@
private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings";
private static final String KEY_UNLOCK_PHONE = "biometric_settings_biometric_keyguard";
private static final String KEY_USE_IN_APPS = "biometric_settings_biometric_app";
+ private static final String KEY_INTRO_PREFERENCE = "biometric_intro";
+
+ private ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
+ @Nullable private ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
@Override
public void onAttach(Context context) {
@@ -41,6 +52,41 @@
}
@Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(getActivity());
+ if (mActiveUnlockStatusUtils.isAvailable()) {
+ updateUiForActiveUnlock();
+ }
+ }
+
+ private void updateUiForActiveUnlock() {
+ OnContentChangedListener listener = new OnContentChangedListener() {
+ @Override
+ public void onContentChanged(String newValue) {
+ updateUnlockPhonePreferenceSummary();
+ }
+ };
+
+ mActiveUnlockDeviceNameListener =
+ new ActiveUnlockDeviceNameListener(getActivity(), listener);
+ mActiveUnlockDeviceNameListener.subscribe();
+ final Preference introPreference = findPreference(KEY_INTRO_PREFERENCE);
+ if (introPreference != null) {
+ introPreference.setTitle(mActiveUnlockStatusUtils.getIntroForActiveUnlock());
+ }
+ getActivity().setTitle(mActiveUnlockStatusUtils.getTitleForActiveUnlock());
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mActiveUnlockDeviceNameListener != null) {
+ mActiveUnlockDeviceNameListener.unsubscribe();
+ }
+ super.onDestroy();
+ }
+
+ @Override
protected int getPreferenceScreenResId() {
return R.xml.security_settings_combined_biometric;
}
@@ -75,6 +121,16 @@
return SettingsEnums.COMBINED_BIOMETRIC;
}
+ @Override
+ protected String getUseAnyBiometricSummary() {
+ // either Active Unlock is not enabled or no device is enrolled.
+ if (mActiveUnlockDeviceNameListener == null
+ || !mActiveUnlockDeviceNameListener.hasEnrolled()) {
+ return super.getUseAnyBiometricSummary();
+ }
+ return mActiveUnlockStatusUtils.getUnlockDeviceSummaryForActiveUnlock();
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric);
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
index a2907f8..d420f78 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
@@ -32,6 +32,7 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.R;
import com.android.settings.testutils.ActiveUnlockTestUtils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -135,4 +136,90 @@
assertThat(mActiveUnlockStatusUtils.useUnlockIntentLayout()).isFalse();
assertThat(mActiveUnlockStatusUtils.useBiometricFailureLayout()).isTrue();
}
+
+ @Test
+ public void getTitle_faceEnabled_returnsFacePreferenceTitle() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mActiveUnlockStatusUtils.getTitleForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.security_settings_face_preference_title));
+ }
+
+ @Test
+ public void getTitle_fingerprintEnabled_returnsFingerprintPreferenceTitle() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mActiveUnlockStatusUtils.getTitleForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.security_settings_fingerprint_preference_title));
+ }
+
+ @Test
+ public void getIntro_faceEnabled_returnsIntroWithFace() {
+ ActiveUnlockTestUtils.enable(
+ mApplicationContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mActiveUnlockStatusUtils.getIntroForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.biometric_settings_intro_with_face));
+ }
+
+ @Test
+ public void getIntro_fingerprintEnabled_returnsIntroWithFingerprint() {
+ ActiveUnlockTestUtils.enable(
+ mApplicationContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mActiveUnlockStatusUtils.getIntroForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.biometric_settings_intro_with_fingerprint));
+ }
+
+ @Test
+ public void getIntro_unlockOnIntentAndFaceEnabled_returnsEmpty() {
+ ActiveUnlockTestUtils.enable(
+ mApplicationContext, ActiveUnlockStatusUtils.UNLOCK_INTENT_LAYOUT);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mActiveUnlockStatusUtils.getIntroForActiveUnlock()).isEqualTo("");
+ }
+
+ @Test
+ public void getIntro_unlockOnIntentAndFaceAndFingerprintEnabled_returnsDefault() {
+ ActiveUnlockTestUtils.enable(
+ mApplicationContext, ActiveUnlockStatusUtils.UNLOCK_INTENT_LAYOUT);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mActiveUnlockStatusUtils.getIntroForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.biometric_settings_intro));
+ }
+
+ @Test
+ public void getUnlockDeviceSummary_fingerprintEnabled_returnsFingerprintOrWatch() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mActiveUnlockStatusUtils.getUnlockDeviceSummaryForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.biometric_settings_use_fingerprint_or_watch_preference_summary));
+ }
+
+ @Test
+ public void getUnlockDeviceSummary_faceEnabled_returnsFaceOrWatch() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mActiveUnlockStatusUtils.getUnlockDeviceSummaryForActiveUnlock())
+ .isEqualTo(mApplicationContext.getString(
+ R.string.biometric_settings_use_face_or_watch_preference_summary));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/FakeContentProvider.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/FakeContentProvider.java
index 07b79da..7bb6941 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/FakeContentProvider.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/FakeContentProvider.java
@@ -39,6 +39,8 @@
.build();
public static final String METHOD_SUMMARY = "getSummary";
public static final String KEY_SUMMARY = "com.android.settings.summary";
+ private static final String METHOD_DEVICE_NAME = "getDeviceName";
+ private static final String KEY_DEVICE_NAME = "com.android.settings.active_unlock.device_name";
@Nullable private static String sTileSummary;
@Nullable private static String sDeviceName;
@@ -50,10 +52,15 @@
sTileSummary = summary;
}
+ public static void setDeviceName(String deviceName) {
+ sDeviceName = deviceName;
+ }
+
public static void init(Context context) {
Settings.Secure.putString(
context.getContentResolver(), ActiveUnlockTestUtils.PROVIDER_SETTING, AUTHORITY);
sTileSummary = null;
+ sDeviceName = null;
}
@Override
@@ -61,6 +68,8 @@
Bundle bundle = new Bundle();
if (METHOD_SUMMARY.equals(method)) {
bundle.putCharSequence(KEY_SUMMARY, sTileSummary);
+ } else if (METHOD_DEVICE_NAME.equals(method)) {
+ bundle.putCharSequence(KEY_DEVICE_NAME, sDeviceName);
}
return bundle;
}