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
This commit is contained in:
Derek Jedral
2023-01-24 22:00:01 -08:00
parent 334e48f4ff
commit 9134f24f31
7 changed files with 288 additions and 5 deletions

View File

@@ -20,6 +20,7 @@
android:title="@string/security_settings_biometric_preference_title"> android:title="@string/security_settings_biometric_preference_title">
<com.android.settingslib.widget.TopIntroPreference <com.android.settingslib.widget.TopIntroPreference
android:key="biometric_intro"
android:title="@string/biometric_settings_intro" /> android:title="@string/biometric_settings_intro" />
<PreferenceCategory <PreferenceCategory

View File

@@ -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();
}
}

View File

@@ -31,7 +31,9 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.BasePreferenceController.AvailabilityStatus; import com.android.settings.core.BasePreferenceController.AvailabilityStatus;
@@ -164,6 +166,82 @@ public class ActiveUnlockStatusUtils {
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE; 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() { private static String getFlagState() {
return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME); return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
} }

View File

@@ -107,10 +107,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
launchChooseOrConfirmLock(); launchChooseOrConfirmLock();
} }
final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey()); updateUnlockPhonePreferenceSummary();
if (unlockPhonePreference != null) {
unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
}
final Preference useInAppsPreference = findPreference(getUseInAppsPreferenceKey()); final Preference useInAppsPreference = findPreference(getUseInAppsPreferenceKey());
if (useInAppsPreference != null) { if (useInAppsPreference != null) {
@@ -309,8 +306,15 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
} }
} }
protected void updateUnlockPhonePreferenceSummary() {
final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
if (unlockPhonePreference != null) {
unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
}
}
@NonNull @NonNull
private String getUseAnyBiometricSummary() { protected String getUseAnyBiometricSummary() {
boolean isFaceAllowed = mFaceManager != null && mFaceManager.isHardwareDetected(); boolean isFaceAllowed = mFaceManager != null && mFaceManager.isHardwareDetected();
boolean isFingerprintAllowed = boolean isFingerprintAllowed =
mFingerprintManager != null && mFingerprintManager.isHardwareDetected(); mFingerprintManager != null && mFingerprintManager.isHardwareDetected();

View File

@@ -17,8 +17,15 @@ package com.android.settings.biometrics.combination;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; 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.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.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
@@ -32,6 +39,10 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings"; 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_UNLOCK_PHONE = "biometric_settings_biometric_keyguard";
private static final String KEY_USE_IN_APPS = "biometric_settings_biometric_app"; 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 @Override
public void onAttach(Context context) { public void onAttach(Context context) {
@@ -40,6 +51,41 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
use(BiometricSettingsAppPreferenceController.class).setUserId(mUserId); use(BiometricSettingsAppPreferenceController.class).setUserId(mUserId);
} }
@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 @Override
protected int getPreferenceScreenResId() { protected int getPreferenceScreenResId() {
return R.xml.security_settings_combined_biometric; return R.xml.security_settings_combined_biometric;
@@ -75,6 +121,16 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
return SettingsEnums.COMBINED_BIOMETRIC; 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 = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric); new CombinedBiometricSearchIndexProvider(R.xml.security_settings_combined_biometric);
} }

View File

@@ -32,6 +32,7 @@ import android.hardware.fingerprint.FingerprintManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.ActiveUnlockTestUtils; import com.android.settings.testutils.ActiveUnlockTestUtils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig; import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -135,4 +136,90 @@ public class ActiveUnlockStatusUtilsTest {
assertThat(mActiveUnlockStatusUtils.useUnlockIntentLayout()).isFalse(); assertThat(mActiveUnlockStatusUtils.useUnlockIntentLayout()).isFalse();
assertThat(mActiveUnlockStatusUtils.useBiometricFailureLayout()).isTrue(); 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));
}
} }

View File

@@ -39,6 +39,8 @@ public final class FakeContentProvider extends ContentProvider {
.build(); .build();
public static final String METHOD_SUMMARY = "getSummary"; public static final String METHOD_SUMMARY = "getSummary";
public static final String KEY_SUMMARY = "com.android.settings.summary"; 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 sTileSummary;
@Nullable private static String sDeviceName; @Nullable private static String sDeviceName;
@@ -50,10 +52,15 @@ public final class FakeContentProvider extends ContentProvider {
sTileSummary = summary; sTileSummary = summary;
} }
public static void setDeviceName(String deviceName) {
sDeviceName = deviceName;
}
public static void init(Context context) { public static void init(Context context) {
Settings.Secure.putString( Settings.Secure.putString(
context.getContentResolver(), ActiveUnlockTestUtils.PROVIDER_SETTING, AUTHORITY); context.getContentResolver(), ActiveUnlockTestUtils.PROVIDER_SETTING, AUTHORITY);
sTileSummary = null; sTileSummary = null;
sDeviceName = null;
} }
@Override @Override
@@ -61,6 +68,8 @@ public final class FakeContentProvider extends ContentProvider {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
if (METHOD_SUMMARY.equals(method)) { if (METHOD_SUMMARY.equals(method)) {
bundle.putCharSequence(KEY_SUMMARY, sTileSummary); bundle.putCharSequence(KEY_SUMMARY, sTileSummary);
} else if (METHOD_DEVICE_NAME.equals(method)) {
bundle.putCharSequence(KEY_DEVICE_NAME, sDeviceName);
} }
return bundle; return bundle;
} }