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;
     }