Merge "Customize ChooseLockGeneric SUW options" into udc-dev

This commit is contained in:
Treehugger Robot
2023-04-17 06:59:18 +00:00
committed by Android (Google) Code Review
4 changed files with 223 additions and 5 deletions

View File

@@ -746,6 +746,8 @@
<!-- Fingerprint enrollment and settings --><skip /> <!-- Fingerprint enrollment and settings --><skip />
<!-- Note: Update FingerprintEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. --> <!-- Note: Update FingerprintEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. -->
<!-- Title shown for choose lock options [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint">Fingerprint</string>
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] --> <!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_preference_title">Fingerprint</string> <string name="security_settings_fingerprint_preference_title">Fingerprint</string>
<!-- Title for a category shown for the fingerprint settings page, followed by items that the user can toggle on/off to require/disable. [CHAR LIMIT=50] --> <!-- Title for a category shown for the fingerprint settings page, followed by items that the user can toggle on/off to require/disable. [CHAR LIMIT=50] -->

View File

@@ -48,6 +48,8 @@ import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.service.persistentdata.PersistentDataBlockManager; import android.service.persistentdata.PersistentDataBlockManager;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.EventLog; import android.util.EventLog;
import android.util.Log; import android.util.Log;
@@ -56,6 +58,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@@ -76,6 +79,7 @@ import com.android.settings.biometrics.BiometricEnrollActivity;
import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.LockScreenSafetySource; import com.android.settings.safetycenter.LockScreenSafetySource;
import com.android.settings.search.SearchFeatureProvider; import com.android.settings.search.SearchFeatureProvider;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
@@ -139,6 +143,9 @@ public class ChooseLockGeneric extends SettingsActivity {
*/ */
public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras"; public static final String EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS = "choose_lock_generic_extras";
/** The character ' • ' to separate the setup choose options */
public static final String SEPARATOR = " \u2022 ";
@VisibleForTesting @VisibleForTesting
static final int CONFIRM_EXISTING_REQUEST = 100; static final int CONFIRM_EXISTING_REQUEST = 100;
@VisibleForTesting @VisibleForTesting
@@ -628,10 +635,11 @@ public class ChooseLockGeneric extends SettingsActivity {
R.string.face_unlock_set_unlock_password); R.string.face_unlock_set_unlock_password);
} else if (mForBiometrics) { } else if (mForBiometrics) {
setPreferenceTitle(ScreenLockType.PATTERN, setPreferenceTitle(ScreenLockType.PATTERN,
R.string.biometrics_unlock_set_unlock_pattern); getBiometricsPreferenceTitle(ScreenLockType.PATTERN));
setPreferenceTitle(ScreenLockType.PIN, R.string.biometrics_unlock_set_unlock_pin); setPreferenceTitle(ScreenLockType.PIN,
getBiometricsPreferenceTitle(ScreenLockType.PIN));
setPreferenceTitle(ScreenLockType.PASSWORD, setPreferenceTitle(ScreenLockType.PASSWORD,
R.string.biometrics_unlock_set_unlock_password); getBiometricsPreferenceTitle(ScreenLockType.PASSWORD));
} }
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) { if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
@@ -652,6 +660,36 @@ public class ChooseLockGeneric extends SettingsActivity {
} }
} }
@VisibleForTesting
String getBiometricsPreferenceTitle(@NonNull ScreenLockType secureType) {
SpannableStringBuilder ssb = new SpannableStringBuilder();
BidiFormatter bidi = BidiFormatter.getInstance();
// Assume the flow is "Screen Lock" + "Face" + "Fingerprint"
if (mController != null) {
ssb.append(bidi.unicodeWrap(mController.getTitle(secureType)));
} else {
Log.e(TAG, "ChooseLockGenericController is null!");
}
if (mFaceManager != null && mFaceManager.isHardwareDetected() && isFaceSupported()) {
ssb.append(bidi.unicodeWrap(SEPARATOR));
ssb.append(bidi.unicodeWrap(
getResources().getString(R.string.keywords_face_settings)));
}
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
ssb.append(bidi.unicodeWrap(SEPARATOR));
ssb.append(bidi.unicodeWrap(
getResources().getString(R.string.security_settings_fingerprint)));
}
return ssb.toString();
}
private boolean isFaceSupported() {
return FeatureFactory.getFactory(getContext().getApplicationContext())
.getFaceFeatureProvider()
.isSetupWizardSupported(getContext().getApplicationContext());
}
private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) { private void setPreferenceTitle(ScreenLockType lock, @StringRes int title) {
Preference preference = findPreference(lock.preferenceKey); Preference preference = findPreference(lock.preferenceKey);
if (preference != null) { if (preference != null) {

View File

@@ -29,11 +29,14 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER; import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application; import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
@@ -43,6 +46,8 @@ import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy; import android.app.admin.PasswordPolicy;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings.Global; import android.provider.Settings.Global;
@@ -55,6 +60,7 @@ import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase; import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment; import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
import com.android.settings.search.SearchFeatureProvider; import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowStorageManager; import com.android.settings.testutils.shadow.ShadowStorageManager;
@@ -65,8 +71,12 @@ import com.android.settingslib.widget.FooterPreference;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric; import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@@ -86,13 +96,33 @@ import org.robolectric.shadows.ShadowPersistentDataBlockManager;
@Ignore("b/179136903: Tests failed with collapsing toolbar, plan to figure out root cause later.") @Ignore("b/179136903: Tests failed with collapsing toolbar, plan to figure out root cause later.")
public class ChooseLockGenericTest { public class ChooseLockGenericTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private FakeFeatureFactory mFakeFeatureFactory;
private ChooseLockGenericFragment mFragment; private ChooseLockGenericFragment mFragment;
private ChooseLockGeneric mActivity; private ChooseLockGeneric mActivity;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
@Before @Before
public void setUp() { public void setUp() {
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mActivity = Robolectric.buildActivity(ChooseLockGeneric.class)
.create()
.start()
.postCreate(null)
.resume()
.get();
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1); Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
mFragment = new ChooseLockGenericFragment(); mFragment = new ChooseLockGenericFragment();
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn(
false);
} }
@After @After
@@ -490,13 +520,47 @@ public class ChooseLockGenericTest {
new PasswordMetrics(CREDENTIAL_TYPE_NONE)); new PasswordMetrics(CREDENTIAL_TYPE_NONE));
} }
@Test
public void updatePreferenceText_supportBiometrics_showFaceAndFingerprint() {
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
final PasswordPolicy policy = new PasswordPolicy();
policy.quality = PASSWORD_QUALITY_ALPHABETIC;
ShadowLockPatternUtils.setRequestedProfilePasswordMetrics(policy.getMinMetrics());
final Intent intent = new Intent().putExtra(EXTRA_KEY_FOR_BIOMETRICS, true);
initActivity(intent);
final Intent passwordIntent = mFragment.getLockPatternIntent();
assertThat(passwordIntent.getIntExtra(ChooseLockPassword.EXTRA_KEY_MIN_COMPLEXITY,
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_LOW);
final String supportFingerprint = mActivity.getResources().getString(
R.string.security_settings_fingerprint);
final String supportFace = mActivity.getResources().getString(
R.string.keywords_face_settings);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFingerprint);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFace);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFingerprint);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFace);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFingerprint);
assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFace);
}
private void initActivity(@Nullable Intent intent) { private void initActivity(@Nullable Intent intent) {
if (intent == null) { if (intent == null) {
intent = new Intent(); intent = new Intent();
} }
intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false); intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false);
// TODO(b/275023433) This presents the activity from being made 'visible` is workaround
mActivity = Robolectric.buildActivity(ChooseLockGeneric.InternalActivity.class, intent) mActivity = Robolectric.buildActivity(ChooseLockGeneric.InternalActivity.class, intent)
.setup().get(); .create().start().postCreate(null).resume().get();
mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commitNow(); mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commitNow();
} }
} }

View File

@@ -23,13 +23,22 @@ import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_R
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
import android.content.Intent; import android.content.Intent;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment; import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowPasswordUtils; import com.android.settings.testutils.shadow.ShadowPasswordUtils;
import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -38,8 +47,13 @@ import com.android.settings.testutils.shadow.ShadowUtils;
import com.google.android.setupdesign.GlifPreferenceLayout; import com.google.android.setupdesign.GlifPreferenceLayout;
import org.junit.After; import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric; import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows; import org.robolectric.Shadows;
@@ -56,6 +70,28 @@ import java.util.List;
}) })
public class SetupChooseLockGenericTest { public class SetupChooseLockGenericTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private FakeFeatureFactory mFakeFeatureFactory;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
@Before
public void setUp() {
ShadowUtils.setFingerprintManager(mFingerprintManager);
ShadowUtils.setFaceManager(mFaceManager);
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
Settings.Global.putInt(application.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
0);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
}
@After @After
public void tearDown() { public void tearDown() {
ShadowPasswordUtils.reset(); ShadowPasswordUtils.reset();
@@ -102,13 +138,91 @@ public class SetupChooseLockGenericTest {
assertThat(view.getDescriptionText().toString()).isEqualTo(fragment.loadDescriptionText()); assertThat(view.getDescriptionText().toString()).isEqualTo(fragment.loadDescriptionText());
} }
@Test
public void updatePreferenceTextShowScreenLockAndFingerprint() {
when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn(
false);
SetupChooseLockGenericFragment fragment = getFragmentOfSetupChooseLockGeneric(true);
final String supportFingerprint = fragment.getResources().getString(
R.string.security_settings_fingerprint);
final String supportFace = fragment.getResources().getString(
R.string.keywords_face_settings);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).doesNotContain(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).doesNotContain(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).doesNotContain(
supportFace);
}
@Test
public void updatePreferenceTextShowScreenLockAndShowFaceAndShowFingerprint() {
when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn(
true);
SetupChooseLockGenericFragment fragment = getFragmentOfSetupChooseLockGeneric(true);
final String supportFingerprint = fragment.getResources().getString(
R.string.security_settings_fingerprint);
final String supportFace = fragment.getResources().getString(
R.string.keywords_face_settings);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFace);
}
@Test
public void updatePreferenceTextShowScreenLockAndShowFingerprint() {
when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn(
false);
SetupChooseLockGenericFragment fragment = getFragmentOfSetupChooseLockGeneric(true);
final String supportFingerprint = fragment.getResources().getString(
R.string.security_settings_fingerprint);
final String supportFace = fragment.getResources().getString(
R.string.keywords_face_settings);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).doesNotContain(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).doesNotContain(
supportFace);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains(
supportFingerprint);
assertThat(fragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).doesNotContain(
supportFace);
}
private SetupChooseLockGenericFragment getFragmentOfSetupChooseLockGeneric(boolean biometric) { private SetupChooseLockGenericFragment getFragmentOfSetupChooseLockGeneric(boolean biometric) {
ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY); ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN"); Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH); intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, biometric); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, biometric);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, biometric);
// TODO(b/275023433) This presents the activity from being made 'visible` is workaround
SetupChooseLockGeneric activity = SetupChooseLockGeneric activity =
Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).setup().get(); Robolectric.buildActivity(SetupChooseLockGeneric.class,
intent).create().start().postCreate(null).resume().get();
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments(); List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
assertThat(fragments).isNotNull(); assertThat(fragments).isNotNull();