Refine SkipDialog title and desc by device configs

1. Wrap isFaceSupportedInSUW() in Settings Utils
2. Wrap getCombinedScreenLockOptions in Settings Utils
3. Add EXTRA_KEY_FOR_SUW to judge if in SUW flow
4. Refactor SetupSkipDialog by hasFace, hasFingerprint,
   isSuw, isFaceSupported conditions
5. Clean up the mapping logic of SetupSkipDialog
6. Replace bools with @LockPatternUtils.CredentialType
7. Refine the logic for isFaceSupported
   ---------------------------------------
   Config |SuwSupportFace|!SuwSupportFace|
    isSuw |    true      |      false    |
   !isSuw |   hasFace    |     hasFace   |

Bug: 263070591
Bug: 279389803
Bug: 279195215
Test: adb shell am start -a android.settings.BIOMETRIC_ENROLL
Test: SUW(workprofile), post-SUW
Test: m RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.password
Test: m RunSettingsRoboTests ROBOTEST_FILTER=SetupSkipDialogTest
Change-Id: Ie7af4299695dc3983b4190929b4dd659c301c082
This commit is contained in:
lbill
2023-04-21 10:21:07 +00:00
parent 5801e98d7f
commit 67d6dff7cc
13 changed files with 359 additions and 201 deletions

View File

@@ -28,6 +28,8 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.Surface;
@@ -38,6 +40,7 @@ import androidx.fragment.app.FragmentActivity;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
@@ -45,6 +48,7 @@ import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.SetupChooseLockGeneric;
@@ -60,6 +64,9 @@ import java.lang.annotation.RetentionPolicy;
public class BiometricUtils {
private static final String TAG = "BiometricUtils";
/** The character ' • ' to separate the setup choose options */
public static final String SEPARATOR = " \u2022 ";
// Note: Theis IntDef must align SystemUI DevicePostureInt
@IntDef(prefix = {"DEVICE_POSTURE_"}, value = {
DEVICE_POSTURE_UNKNOWN,
@@ -496,4 +503,45 @@ public class BiometricUtils {
public static boolean isLandscape(@NonNull Context context) {
return context.getDisplay().getRotation() == Surface.ROTATION_90;
}
/**
* Returns true if the device supports Face enrollment in SUW flow
*/
public static boolean isFaceSupportedInSuw(Context context) {
return FeatureFactory.getFactory(context).getFaceFeatureProvider().isSetupWizardSupported(
context);
}
/**
* Returns the combined screen lock options by device biometrics config
* @param context the application context
* @param screenLock the type of screen lock(PIN, Pattern, Password) in string
* @param hasFingerprint device support fingerprint or not
* @param isFaceSupported device support face or not
* @return the options combined with screen lock, face, and fingerprint in String format.
*/
public static String getCombinedScreenLockOptions(Context context,
CharSequence screenLock, boolean hasFingerprint, boolean isFaceSupported) {
final SpannableStringBuilder ssb = new SpannableStringBuilder();
final BidiFormatter bidi = BidiFormatter.getInstance();
// Assume the flow is "Screen Lock" + "Face" + "Fingerprint"
ssb.append(bidi.unicodeWrap(screenLock));
if (isFaceSupported) {
ssb.append(bidi.unicodeWrap(SEPARATOR));
ssb.append(bidi.unicodeWrap(
capitalize(context.getString(R.string.keywords_face_settings))));
}
if (hasFingerprint) {
ssb.append(bidi.unicodeWrap(SEPARATOR));
ssb.append(bidi.unicodeWrap(
capitalize(context.getString(R.string.security_settings_fingerprint))));
}
return ssb.toString();
}
private static String capitalize(final String input) {
return Character.toUpperCase(input.charAt(0)) + input.substring(1);
}
}

View File

@@ -48,8 +48,6 @@ 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;
@@ -77,9 +75,9 @@ import com.android.settings.SetupWizardUtils;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollActivity;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
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;
@@ -143,9 +141,6 @@ 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
@@ -662,32 +657,20 @@ public class ChooseLockGeneric extends SettingsActivity {
@VisibleForTesting
String getBiometricsPreferenceTitle(@NonNull ScreenLockType secureType) {
SpannableStringBuilder ssb = new SpannableStringBuilder();
BidiFormatter bidi = BidiFormatter.getInstance();
final boolean hasFingerprint = Utils.hasFingerprintHardware(getContext());
final boolean hasFace = Utils.hasFaceHardware(getContext());
final boolean isSuw = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isFaceSupported =
hasFace && (!isSuw || BiometricUtils.isFaceSupportedInSuw(getContext()));
// Assume the flow is "Screen Lock" + "Face" + "Fingerprint"
if (mController != null) {
ssb.append(bidi.unicodeWrap(mController.getTitle(secureType)));
return BiometricUtils.getCombinedScreenLockOptions(getContext(),
mController.getTitle(secureType), hasFingerprint, isFaceSupported);
} else {
Log.e(TAG, "ChooseLockGenericController is null!");
return getResources().getString(R.string.error_title);
}
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) {

View File

@@ -64,6 +64,8 @@ public final class ChooseLockSettingsHelper {
public static final String EXTRA_KEY_FOR_FACE = "for_face";
// For the paths where multiple biometric sensors exist
public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics";
// For the paths where setup biometrics in suw flow
public static final String EXTRA_KEY_IS_SUW = "is_suw";
public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
public static final String EXTRA_KEY_REQUEST_GK_PW_HANDLE = "request_gk_pw_handle";
// Gatekeeper password handle, which can subsequently be used to generate Gatekeeper

View File

@@ -19,6 +19,7 @@ package com.android.settings.password;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import android.app.RemoteServiceException.MissingRequestPasswordComplexityPermissionException;
@@ -43,6 +44,7 @@ import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.utils.SettingsDividerItemDecoration;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifPreferenceLayout;
import com.google.android.setupdesign.util.ThemeHelper;
@@ -187,14 +189,14 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
final String key = preference.getKey();
if (KEY_UNLOCK_SET_DO_LATER.equals(key)) {
// show warning.
final Intent intent = getActivity().getIntent();
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
getActivity().getIntent()
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
/* isPatternMode= */ false,
/* isAlphaMode= */ false,
CREDENTIAL_TYPE_NONE,
intent.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false),
/* forFingerprint= */ false,
/* forFace= */ false,
/* forBiometrics= */ false
/* forBiometrics= */ false,
WizardManagerHelper.isAnySetupWizard(intent)
);
dialog.show(getFragmentManager());
return true;

View File

@@ -16,6 +16,9 @@
package com.android.settings.password;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +34,8 @@ import com.android.settings.R;
import com.android.settings.SetupRedactionInterstitial;
import com.android.settings.password.ChooseLockTypeDialogFragment.OnLockTypeSelectedListener;
import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Setup Wizard's version of ChooseLockPassword screen. It inherits the logic and basic structure
* from ChooseLockPassword class, and should remain similar to that behaviorally. This class should
@@ -113,12 +118,12 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
final boolean forBiometrics = intent
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
mIsAlphaMode ? CREDENTIAL_TYPE_PASSWORD : CREDENTIAL_TYPE_PIN,
frpSupported,
/* isPatternMode= */ false,
mIsAlphaMode,
forFingerprint,
forFace,
forBiometrics);
forBiometrics,
WizardManagerHelper.isAnySetupWizard(intent));
ConfirmDeviceCredentialUtils.hideImeImmediately(
getActivity().getWindow().getDecorView());

View File

@@ -18,6 +18,8 @@ package com.android.settings.password;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -32,6 +34,8 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.SetupRedactionInterstitial;
import com.google.android.setupcompat.util.WizardManagerHelper;
/**
* Setup Wizard's version of ChooseLockPattern screen. It inherits the logic and basic structure
* from ChooseLockPattern class, and should remain similar to that behaviorally. This class should
@@ -101,14 +105,13 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
final boolean forBiometrics = intent
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
final SetupSkipDialog dialog = SetupSkipDialog.newInstance(
CREDENTIAL_TYPE_PATTERN,
frpSupported,
/* isPatternMode= */ true,
/* isAlphaMode= */ false,
forFingerprint,
forFace,
forBiometrics);
forBiometrics,
WizardManagerHelper.isAnySetupWizard(intent));
dialog.show(getFragmentManager());
return;
}

View File

@@ -16,6 +16,14 @@
package com.android.settings.password;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_SUW;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -29,7 +37,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class SetupSkipDialog extends InstrumentedDialogFragment
@@ -38,24 +49,23 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
public static final String EXTRA_FRP_SUPPORTED = ":settings:frp_supported";
private static final String ARG_FRP_SUPPORTED = "frp_supported";
// The key indicates type of lock screen is pattern setup.
private static final String ARG_LOCK_TYPE_PATTERN = "lock_type_pattern";
// The key indicates type of screen lock credential types(PIN/Pattern/Password)
private static final String ARG_LOCK_CREDENTIAL_TYPE = "lock_credential_type";
// The key indicates type of lock screen setup is alphanumeric for password setup.
private static final String ARG_LOCK_TYPE_ALPHANUMERIC = "lock_type_alphanumeric";
private static final String TAG_SKIP_DIALOG = "skip_dialog";
public static final int RESULT_SKIP = Activity.RESULT_FIRST_USER + 10;
public static SetupSkipDialog newInstance(boolean isFrpSupported, boolean isPatternMode,
boolean isAlphanumericMode, boolean forFingerprint, boolean forFace,
boolean forBiometrics) {
public static SetupSkipDialog newInstance(@LockPatternUtils.CredentialType int credentialType,
boolean isFrpSupported, boolean forFingerprint, boolean forFace,
boolean forBiometrics, boolean isSuw) {
SetupSkipDialog dialog = new SetupSkipDialog();
Bundle args = new Bundle();
args.putInt(ARG_LOCK_CREDENTIAL_TYPE, credentialType);
args.putBoolean(ARG_FRP_SUPPORTED, isFrpSupported);
args.putBoolean(ARG_LOCK_TYPE_PATTERN, isPatternMode);
args.putBoolean(ARG_LOCK_TYPE_ALPHANUMERIC, isAlphanumericMode);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, forFace);
args.putBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
args.putBoolean(EXTRA_KEY_FOR_FINGERPRINT, forFingerprint);
args.putBoolean(EXTRA_KEY_FOR_FACE, forFace);
args.putBoolean(EXTRA_KEY_FOR_BIOMETRICS, forBiometrics);
args.putBoolean(EXTRA_KEY_IS_SUW, isSuw);
dialog.setArguments(args);
return dialog;
}
@@ -70,59 +80,59 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
return onCreateDialogBuilder().create();
}
private AlertDialog.Builder getBiometricsBuilder(
@LockPatternUtils.CredentialType int credentialType, boolean isSuw, boolean hasFace,
boolean hasFingerprint) {
final boolean isFaceSupported = hasFace && (!isSuw || BiometricUtils.isFaceSupportedInSuw(
getContext()));
final int msgResId;
final int screenLockResId;
switch (credentialType) {
case CREDENTIAL_TYPE_PATTERN:
screenLockResId = R.string.unlock_set_unlock_pattern_title;
msgResId = getPatternSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
case CREDENTIAL_TYPE_PASSWORD:
screenLockResId = R.string.unlock_set_unlock_password_title;
msgResId = getPasswordSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
case CREDENTIAL_TYPE_PIN:
default:
screenLockResId = R.string.unlock_set_unlock_pin_title;
msgResId = getPinSkipMessageRes(hasFace && isFaceSupported, hasFingerprint);
break;
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_lock_screen_dialog_button_label, this)
.setNegativeButton(R.string.cancel_lock_screen_dialog_button_label, this)
.setTitle(getSkipSetupTitle(screenLockResId, hasFingerprint,
hasFace && isFaceSupported))
.setMessage(msgResId);
}
@NonNull
public AlertDialog.Builder onCreateDialogBuilder() {
Bundle args = getArguments();
final boolean forFace =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE);
final boolean forFingerprint =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT);
final boolean forBiometrics =
args.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS);
final boolean isSuw = args.getBoolean(EXTRA_KEY_IS_SUW);
final boolean forBiometrics = args.getBoolean(EXTRA_KEY_FOR_BIOMETRICS);
final boolean forFace = args.getBoolean(EXTRA_KEY_FOR_FACE);
final boolean forFingerprint = args.getBoolean(EXTRA_KEY_FOR_FINGERPRINT);
@LockPatternUtils.CredentialType
final int credentialType = args.getInt(ARG_LOCK_CREDENTIAL_TYPE);
if (forFace || forFingerprint || forBiometrics) {
final boolean hasFace = forFace || forBiometrics;
final boolean hasFingerprint = forFingerprint || forBiometrics;
final int titleId;
final int msgResId;
if (args.getBoolean(ARG_LOCK_TYPE_PATTERN)) {
titleId = getPatternSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPatternSkipMessageRes(hasFace, hasFingerprint);
} else if (args.getBoolean(ARG_LOCK_TYPE_ALPHANUMERIC)) {
titleId = getPasswordSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPasswordSkipMessageRes(hasFace, hasFingerprint);
} else {
titleId = getPinSkipTitleRes(hasFace, hasFingerprint);
msgResId = getPinSkipMessageRes(hasFace, hasFingerprint);
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_lock_screen_dialog_button_label, this)
.setNegativeButton(R.string.cancel_lock_screen_dialog_button_label, this)
.setTitle(titleId)
.setMessage(msgResId);
} else {
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_anyway_button_label, this)
.setNegativeButton(R.string.go_back_button_label, this)
.setTitle(R.string.lock_screen_intro_skip_title)
.setMessage(args.getBoolean(ARG_FRP_SUPPORTED) ?
R.string.lock_screen_intro_skip_dialog_text_frp :
R.string.lock_screen_intro_skip_dialog_text);
final boolean hasFace = Utils.hasFaceHardware(getContext());
final boolean hasFingerprint = Utils.hasFingerprintHardware(getContext());
return getBiometricsBuilder(credentialType, isSuw, hasFace, hasFingerprint);
}
}
@StringRes
private int getPatternSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_pattern_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_pattern_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_pattern_skip_fingerprint_title;
} else {
return R.string.lock_screen_pattern_skip_title;
}
return new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.skip_anyway_button_label, this)
.setNegativeButton(R.string.go_back_button_label, this)
.setTitle(R.string.lock_screen_intro_skip_title)
.setMessage(args.getBoolean(ARG_FRP_SUPPORTED) ?
R.string.lock_screen_intro_skip_dialog_text_frp :
R.string.lock_screen_intro_skip_dialog_text);
}
@StringRes
@@ -138,19 +148,6 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
@StringRes
private int getPasswordSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_password_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_password_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_password_skip_fingerprint_title;
} else {
return R.string.lock_screen_password_skip_title;
}
}
@StringRes
private int getPasswordSkipMessageRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
@@ -164,19 +161,6 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
@StringRes
private int getPinSkipTitleRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
return R.string.lock_screen_pin_skip_biometrics_title;
} else if (hasFace) {
return R.string.lock_screen_pin_skip_face_title;
} else if (hasFingerprint) {
return R.string.lock_screen_pin_skip_fingerprint_title;
} else {
return R.string.lock_screen_pin_skip_title;
}
}
@StringRes
private int getPinSkipMessageRes(boolean hasFace, boolean hasFingerprint) {
if (hasFace && hasFingerprint) {
@@ -190,6 +174,13 @@ public class SetupSkipDialog extends InstrumentedDialogFragment
}
}
private String getSkipSetupTitle(int screenTypeResId, boolean hasFingerprint,
boolean hasFace) {
return getString(R.string.lock_screen_skip_setup_title,
BiometricUtils.getCombinedScreenLockOptions(getContext(),
getString(screenTypeResId), hasFingerprint, hasFace));
}
@Override
public void onClick(DialogInterface dialog, int button) {
Activity activity = getActivity();