diff --git a/res/values/strings.xml b/res/values/strings.xml index 6d0577b53d8..65d83155e7d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -746,6 +746,8 @@ + + Fingerprint Fingerprint diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index 398e4b096c6..b84b80edef5 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -48,6 +48,8 @@ import android.os.UserHandle; 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.View; 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.BiometricEnrollActivity; 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 class ChooseLockGeneric extends SettingsActivity { */ 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 @@ public class ChooseLockGeneric extends SettingsActivity { 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 @@ 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) { 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 d06ba59afa9..f2894cc20e2 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.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; 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.PasswordMetrics; 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.R; 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 com.android.settingslib.widget.FooterPreference; 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 @@ import org.robolectric.shadows.ShadowPersistentDataBlockManager; @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 @@ public class ChooseLockGenericTest { 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 494d3f0671b..8db3fd75154 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.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_R 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.android.settings.testutils.shadow.ShadowUtils; 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 @@ import java.util.List; }) 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 @@ public class SetupChooseLockGenericTest { 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 fragments = activity.getSupportFragmentManager().getFragments(); assertThat(fragments).isNotNull();