Support configurable multi-stage UDFPS enrollment
Implements multi-stage enrollment for UDFPS. This implementation supports both highlighting the progress bar when a help message is received and configuring the progress thresholds between enroll stages via an XML resource. Test: Manual Bug: 198928407 Bug: 200076382 Change-Id: Ied4dbcb4a523f477c5ca1a9146d24751c6473f35
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.settings.biometrics.fingerprint;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -58,6 +59,8 @@ import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -69,11 +72,16 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
static final String TAG_SIDECAR = "sidecar";
|
||||
|
||||
private static final int PROGRESS_BAR_MAX = 10000;
|
||||
private static final int FINISH_DELAY = 250;
|
||||
/**
|
||||
* Enroll with two center touches before going to guided enrollment.
|
||||
*/
|
||||
private static final int NUM_CENTER_TOUCHES = 2;
|
||||
|
||||
private static final int STAGE_UNKNOWN = -1;
|
||||
private static final int STAGE_CENTER = 0;
|
||||
private static final int STAGE_GUIDED = 1;
|
||||
private static final int STAGE_FINGERTIP = 2;
|
||||
private static final int STAGE_EDGES = 3;
|
||||
|
||||
@IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_EDGES})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface EnrollStage {}
|
||||
|
||||
/**
|
||||
* If we don't see progress during this time, we show an error message to remind the users that
|
||||
@@ -100,6 +108,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
||||
.build();
|
||||
|
||||
private FingerprintManager mFingerprintManager;
|
||||
private boolean mCanAssumeUdfps;
|
||||
@Nullable private ProgressBar mProgressBar;
|
||||
private ObjectAnimator mProgressAnim;
|
||||
@@ -125,9 +134,9 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
|
||||
mFingerprintManager = getSystemService(FingerprintManager.class);
|
||||
final List<FingerprintSensorPropertiesInternal> props =
|
||||
fingerprintManager.getSensorPropertiesInternal();
|
||||
mFingerprintManager.getSensorPropertiesInternal();
|
||||
mCanAssumeUdfps = props.size() == 1 && props.get(0).isAnyUdfpsType();
|
||||
|
||||
mAccessibilityManager = getSystemService(AccessibilityManager.class);
|
||||
@@ -273,7 +282,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
// UDFPS animations are owned by SystemUI
|
||||
if (progress >= PROGRESS_BAR_MAX) {
|
||||
// Wait for any animations in SysUI to finish, then proceed to next page
|
||||
getMainThreadHandler().postDelayed(mDelayedFinishRunnable, FINISH_DELAY);
|
||||
getMainThreadHandler().postDelayed(mDelayedFinishRunnable, getFinishDelay());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -300,8 +309,55 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
}
|
||||
|
||||
private void updateTitleAndDescription() {
|
||||
if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
|
||||
if (mCanAssumeUdfps) {
|
||||
updateTitleAndDescriptionForUdfps();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_start_message);
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_repeat_message);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitleAndDescriptionForUdfps() {
|
||||
switch (getCurrentStage()) {
|
||||
case STAGE_CENTER:
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
|
||||
break;
|
||||
|
||||
case STAGE_GUIDED:
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
|
||||
if (mIsAccessibilityEnabled) {
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_repeat_a11y_message);
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_repeat_message);
|
||||
}
|
||||
break;
|
||||
|
||||
case STAGE_FINGERTIP:
|
||||
setHeaderText(R.string.security_settings_udfps_enroll_fingertip_title);
|
||||
if (isStageHalfCompleted()) {
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_repeat_title);
|
||||
} else {
|
||||
setDescriptionText("");
|
||||
}
|
||||
break;
|
||||
|
||||
case STAGE_EDGES:
|
||||
setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
|
||||
if (isStageHalfCompleted()) {
|
||||
setDescriptionText(
|
||||
R.string.security_settings_fingerprint_enroll_repeat_message);
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_edge_message);
|
||||
}
|
||||
break;
|
||||
|
||||
case STAGE_UNKNOWN:
|
||||
default:
|
||||
// setHeaderText(R.string.security_settings_fingerprint_enroll_udfps_title);
|
||||
// Don't use BiometricEnrollBase#setHeaderText, since that invokes setTitle,
|
||||
// which gets announced for a11y upon entering the page. For UDFPS, we want to
|
||||
@@ -309,41 +365,61 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
getLayout().setHeaderText(
|
||||
R.string.security_settings_fingerprint_enroll_udfps_title);
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
|
||||
|
||||
final CharSequence description = getString(
|
||||
R.string.security_settings_udfps_enroll_a11y);
|
||||
getLayout().getHeaderTextView().setContentDescription(description);
|
||||
setTitle(description);
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_start_message);
|
||||
}
|
||||
} else if (mCanAssumeUdfps && !isCenterEnrollmentComplete()) {
|
||||
if (mIsSetupWizard) {
|
||||
setHeaderText(R.string.security_settings_udfps_enroll_title_one_more_time);
|
||||
} else {
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
|
||||
}
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
|
||||
} else {
|
||||
if (mCanAssumeUdfps) {
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
|
||||
if (mIsAccessibilityEnabled) {
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_repeat_a11y_message);
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_udfps_enroll_repeat_message);
|
||||
}
|
||||
} else {
|
||||
setDescriptionText(R.string.security_settings_fingerprint_enroll_repeat_message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCenterEnrollmentComplete() {
|
||||
@EnrollStage
|
||||
private int getCurrentStage() {
|
||||
if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
|
||||
return STAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
final int progressSteps = mSidecar.getEnrollmentSteps() - mSidecar.getEnrollmentRemaining();
|
||||
if (progressSteps < getStageThresholdSteps(0)) {
|
||||
return STAGE_CENTER;
|
||||
} else if (progressSteps < getStageThresholdSteps(1)) {
|
||||
return STAGE_GUIDED;
|
||||
} else if (progressSteps < getStageThresholdSteps(2)) {
|
||||
return STAGE_FINGERTIP;
|
||||
} else {
|
||||
return STAGE_EDGES;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStageHalfCompleted() {
|
||||
// Prior to first enrollment step.
|
||||
if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
|
||||
return false;
|
||||
}
|
||||
final int stepsEnrolled = mSidecar.getEnrollmentSteps() - mSidecar.getEnrollmentRemaining();
|
||||
return stepsEnrolled >= NUM_CENTER_TOUCHES;
|
||||
|
||||
final int progressSteps = mSidecar.getEnrollmentSteps() - mSidecar.getEnrollmentRemaining();
|
||||
int prevThresholdSteps = 0;
|
||||
for (int i = 0; i < mFingerprintManager.getEnrollStageCount(); i++) {
|
||||
final int thresholdSteps = getStageThresholdSteps(i);
|
||||
if (progressSteps >= prevThresholdSteps && progressSteps < thresholdSteps) {
|
||||
final int adjustedProgress = progressSteps - prevThresholdSteps;
|
||||
final int adjustedThreshold = thresholdSteps - prevThresholdSteps;
|
||||
return adjustedProgress >= adjustedThreshold / 2;
|
||||
}
|
||||
prevThresholdSteps = thresholdSteps;
|
||||
}
|
||||
|
||||
// After last enrollment step.
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getStageThresholdSteps(int index) {
|
||||
if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
|
||||
Log.w(TAG, "getStageThresholdSteps: Enrollment not started yet");
|
||||
return 1;
|
||||
}
|
||||
return Math.round(mSidecar.getEnrollmentSteps()
|
||||
* mFingerprintManager.getEnrollStageThreshold(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -489,8 +565,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
mOrientationEventListener = null;
|
||||
}
|
||||
|
||||
private final Animator.AnimatorListener mProgressAnimationListener
|
||||
= new Animator.AnimatorListener() {
|
||||
private final Animator.AnimatorListener mProgressAnimationListener =
|
||||
new Animator.AnimatorListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) { }
|
||||
@@ -501,7 +577,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mProgressBar.getProgress() >= PROGRESS_BAR_MAX) {
|
||||
mProgressBar.postDelayed(mDelayedFinishRunnable, FINISH_DELAY);
|
||||
mProgressBar.postDelayed(mDelayedFinishRunnable, getFinishDelay());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,6 +585,10 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
public void onAnimationCancel(Animator animation) { }
|
||||
};
|
||||
|
||||
private long getFinishDelay() {
|
||||
return mCanAssumeUdfps ? 400L : 250L;
|
||||
}
|
||||
|
||||
// Give the user a chance to see progress completed before jumping to the next stage.
|
||||
private final Runnable mDelayedFinishRunnable = new Runnable() {
|
||||
@Override
|
||||
|
Reference in New Issue
Block a user