Merge "Update the card preference buttons margin" into udc-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6d0577b..65d8315 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -746,6 +746,8 @@
<!-- Fingerprint enrollment and settings --><skip />
<!-- 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] -->
<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] -->
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index 1650d23..f0c1ed1 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -237,20 +237,7 @@
}
}
}
- if (mHasFeatureFingerprint) {
- final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
- final List<FingerprintSensorPropertiesInternal> fpProperties =
- fpManager.getSensorPropertiesInternal();
- final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
- .getInteger(R.integer.suw_max_fingerprints_enrollable);
- if (!fpProperties.isEmpty()) {
- final int maxEnrolls =
- isSetupWizard ? maxFingerprintsEnrollableIfSUW
- : fpProperties.get(0).maxEnrollmentsPerUser;
- mIsFingerprintEnrollable =
- fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
- }
- }
+ updateFingerprintEnrollable(isSetupWizard);
// TODO(b/195128094): remove this restriction
// Consent can only be recorded when this activity is launched directly from the kids
@@ -289,6 +276,23 @@
}
}
+ private void updateFingerprintEnrollable(boolean isSetupWizard) {
+ if (mHasFeatureFingerprint) {
+ final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
+ final List<FingerprintSensorPropertiesInternal> fpProperties =
+ fpManager.getSensorPropertiesInternal();
+ final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
+ .getInteger(R.integer.suw_max_fingerprints_enrollable);
+ if (!fpProperties.isEmpty()) {
+ final int maxEnrolls =
+ isSetupWizard ? maxFingerprintsEnrollableIfSUW
+ : fpProperties.get(0).maxEnrollmentsPerUser;
+ mIsFingerprintEnrollable =
+ fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
+ }
+ }
+ }
+
private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) {
// If the caller is not setup wizard, and the user has something enrolled, finish.
// Allow parental consent flow to skip this check, since one modality could be consented
@@ -464,12 +468,12 @@
mConfirmingCredentials = false;
final boolean isOk =
isSuccessfulConfirmOrChooseCredential(requestCode, resultCode);
- if (isOk && (mHasFeatureFace || mHasFeatureFingerprint)) {
+ if (isOk && (mIsFaceEnrollable || mIsFingerprintEnrollable)) {
// Apply forward animation during the transition from ChooseLock/ConfirmLock to
// SetupFingerprintEnrollIntroduction/FingerprintEnrollmentActivity
TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH);
updateGatekeeperPasswordHandle(data);
- if (mHasFeatureFingerprint) {
+ if (mIsFingerprintEnrollable) {
launchFingerprintOnlyEnroll();
} else {
launchFaceOnlyEnroll();
@@ -482,8 +486,15 @@
break;
case REQUEST_SINGLE_ENROLL_FINGERPRINT:
mIsSingleEnrolling = false;
+ if (resultCode == BiometricEnrollBase.RESULT_FINISHED) {
+ // FingerprintEnrollIntroduction's visibility is determined by
+ // mIsFingerprintEnrollable. Keep this value up-to-date after a successful
+ // enrollment.
+ updateFingerprintEnrollable(WizardManagerHelper.isAnySetupWizard(getIntent()));
+ }
if ((resultCode == BiometricEnrollBase.RESULT_SKIP
- || resultCode == BiometricEnrollBase.RESULT_FINISHED) && mHasFeatureFace) {
+ || resultCode == BiometricEnrollBase.RESULT_FINISHED)
+ && mIsFaceEnrollable) {
// Apply forward animation during the transition from
// SetupFingerprintEnroll*/FingerprintEnrollmentActivity to
// SetupFaceEnrollIntroduction
@@ -495,7 +506,7 @@
break;
case REQUEST_SINGLE_ENROLL_FACE:
mIsSingleEnrolling = false;
- if (resultCode == Activity.RESULT_CANCELED && mHasFeatureFingerprint) {
+ if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable) {
launchFingerprintOnlyEnroll();
} else {
finishOrLaunchHandToParent(resultCode);
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 398e4b0..b84b80e 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -48,6 +48,8 @@
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.text.BidiFormatter;
+import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -56,6 +58,7 @@
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -76,6 +79,7 @@
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.LockScreenSafetySource;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settingslib.RestrictedPreference;
@@ -139,6 +143,9 @@
*/
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
static final int CONFIRM_EXISTING_REQUEST = 100;
@VisibleForTesting
@@ -628,10 +635,11 @@
R.string.face_unlock_set_unlock_password);
} else if (mForBiometrics) {
setPreferenceTitle(ScreenLockType.PATTERN,
- R.string.biometrics_unlock_set_unlock_pattern);
- setPreferenceTitle(ScreenLockType.PIN, R.string.biometrics_unlock_set_unlock_pin);
+ getBiometricsPreferenceTitle(ScreenLockType.PATTERN));
+ setPreferenceTitle(ScreenLockType.PIN,
+ getBiometricsPreferenceTitle(ScreenLockType.PIN));
setPreferenceTitle(ScreenLockType.PASSWORD,
- R.string.biometrics_unlock_set_unlock_password);
+ getBiometricsPreferenceTitle(ScreenLockType.PASSWORD));
}
if (mManagedPasswordProvider.isSettingManagedPasswordSupported()) {
@@ -652,6 +660,36 @@
}
}
+ @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) {
Preference preference = findPreference(lock.preferenceKey);
if (preference != null) {
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
index d06ba59..f2894cc 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
@@ -29,11 +29,14 @@
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_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_REQUESTED_MIN_COMPLEXITY;
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.Shadows.shadowOf;
@@ -43,6 +46,8 @@
import android.app.admin.PasswordPolicy;
import android.content.Context;
import android.content.Intent;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.provider.Settings.Global;
@@ -55,6 +60,7 @@
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
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.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowStorageManager;
@@ -65,8 +71,12 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
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.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -86,13 +96,33 @@
@Ignore("b/179136903: Tests failed with collapsing toolbar, plan to figure out root cause later.")
public class ChooseLockGenericTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ private FakeFeatureFactory mFakeFeatureFactory;
private ChooseLockGenericFragment mFragment;
private ChooseLockGeneric mActivity;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
@Before
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);
mFragment = new ChooseLockGenericFragment();
+
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn(
+ false);
}
@After
@@ -490,13 +520,47 @@
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) {
if (intent == null) {
intent = new Intent();
}
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)
- .setup().get();
+ .create().start().postCreate(null).resume().get();
mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commitNow();
}
}
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
index 494d3f0..8db3fd7 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockGenericTest.java
@@ -23,13 +23,22 @@
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.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
+import com.android.settings.R;
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.ShadowPasswordUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -38,8 +47,13 @@
import com.google.android.setupdesign.GlifPreferenceLayout;
import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
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.RobolectricTestRunner;
import org.robolectric.Shadows;
@@ -56,6 +70,28 @@
})
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
public void tearDown() {
ShadowPasswordUtils.reset();
@@ -102,13 +138,91 @@
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) {
ShadowPasswordUtils.addGrantedPermission(REQUEST_PASSWORD_COMPLEXITY);
Intent intent = new Intent("com.android.settings.SETUP_LOCK_SCREEN");
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
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 =
- Robolectric.buildActivity(SetupChooseLockGeneric.class, intent).setup().get();
+ Robolectric.buildActivity(SetupChooseLockGeneric.class,
+ intent).create().start().postCreate(null).resume().get();
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
assertThat(fragments).isNotNull();