Merge changes from topics "disable_face_enroll_inSUW", "tuscany_FaceEnrollFoldPage" into tm-qpr-dev
* changes: 3-1/ Impl FoldProvider.FoldCallback for Face enroll activities 2-1/ Add config_suw_support_face_enroll to customize SUW face enroll flow Fix face enroll introduction crash after 10mins
This commit is contained in:
@@ -36,6 +36,7 @@ import androidx.window.embedding.SplitRule;
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.biometrics.face.FaceEnrollIntroductionInternal;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
|
||||
@@ -228,6 +229,8 @@ public class ActivityEmbeddingRulesController {
|
||||
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
|
||||
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
|
||||
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
|
||||
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
|
||||
addActivityFilter(activityFilters, Settings.FaceSettingsInternalActivity.class);
|
||||
addActivityFilter(activityFilters, AvatarPickerActivity.class);
|
||||
mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */));
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.core.InstrumentedActivity;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockPattern;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
@@ -215,11 +216,16 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
|
||||
mIsFaceEnrollable =
|
||||
faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
|
||||
|
||||
// exclude face enrollment from setup wizard if configured as a convenience
|
||||
// isSetupWizard is always false for unicorn enrollment, so if consent is
|
||||
// required check if setup has completed instead.
|
||||
final boolean isSettingUp = isSetupWizard || (mParentalOptionsRequired
|
||||
final boolean parentalConsent = isSetupWizard || (mParentalOptionsRequired
|
||||
&& !WizardManagerHelper.isUserSetupComplete(this));
|
||||
if (parentalConsent && isMultiSensor && mIsFaceEnrollable) {
|
||||
// Exclude face enrollment from setup wizard if feature config not supported
|
||||
// in setup wizard flow, we still allow user enroll faces through settings.
|
||||
mIsFaceEnrollable = FeatureFactory.getFactory(getApplicationContext())
|
||||
.getFaceFeatureProvider()
|
||||
.isSetupWizardSupported(getApplicationContext());
|
||||
Log.d(TAG, "config_suw_support_face_enroll: " + mIsFaceEnrollable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mHasFeatureFingerprint) {
|
||||
|
@@ -38,7 +38,10 @@ import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||
import com.android.settings.core.InstrumentedActivity;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
import com.android.systemui.unfold.updates.FoldProvider;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
@@ -60,8 +63,10 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
public static final String EXTRA_KEY_SENSOR_ID = "sensor_id";
|
||||
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
||||
public static final String EXTRA_KEY_MODALITY = "sensor_modality";
|
||||
public static final String EXTRA_KEY_NEXT_LAUNCHED = "next_launched";
|
||||
public static final String EXTRA_FINISHED_ENROLL_FACE = "finished_enrolling_face";
|
||||
public static final String EXTRA_FINISHED_ENROLL_FINGERPRINT = "finished_enrolling_fingerprint";
|
||||
public static final String EXTRA_LAUNCHED_POSTURE_GUIDANCE = "launched_posture_guidance";
|
||||
|
||||
/**
|
||||
* Used by the choose fingerprint wizard to indicate the wizard is
|
||||
@@ -115,14 +120,25 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
* example, when starting fingerprint enroll after face enroll.
|
||||
*/
|
||||
public static final int ENROLL_NEXT_BIOMETRIC_REQUEST = 6;
|
||||
public static final int REQUEST_POSTURE_GUIDANCE = 7;
|
||||
|
||||
protected boolean mLaunchedConfirmLock;
|
||||
protected boolean mLaunchedPostureGuidance;
|
||||
protected boolean mNextLaunched;
|
||||
protected byte[] mToken;
|
||||
protected int mUserId;
|
||||
protected int mSensorId;
|
||||
@BiometricUtils.DevicePostureInt
|
||||
protected int mDevicePostureState;
|
||||
protected long mChallenge;
|
||||
protected boolean mFromSettingsSummary;
|
||||
protected FooterBarMixin mFooterBarMixin;
|
||||
@Nullable
|
||||
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
|
||||
@Nullable
|
||||
protected Intent mPostureGuidanceIntent = null;
|
||||
@Nullable
|
||||
protected FoldProvider.FoldCallback mFoldCallback = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -139,16 +155,23 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||
}
|
||||
mFromSettingsSummary = getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
||||
if (savedInstanceState != null && mToken == null) {
|
||||
mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
|
||||
mToken = savedInstanceState.getByteArray(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||
mFromSettingsSummary =
|
||||
savedInstanceState.getBoolean(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
||||
mChallenge = savedInstanceState.getLong(EXTRA_KEY_CHALLENGE);
|
||||
mSensorId = savedInstanceState.getInt(EXTRA_KEY_SENSOR_ID);
|
||||
if (savedInstanceState != null) {
|
||||
if (mToken == null) {
|
||||
mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
|
||||
mToken = savedInstanceState.getByteArray(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||
mFromSettingsSummary =
|
||||
savedInstanceState.getBoolean(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
||||
mChallenge = savedInstanceState.getLong(EXTRA_KEY_CHALLENGE);
|
||||
mSensorId = savedInstanceState.getInt(EXTRA_KEY_SENSOR_ID);
|
||||
}
|
||||
mLaunchedPostureGuidance = savedInstanceState.getBoolean(
|
||||
EXTRA_LAUNCHED_POSTURE_GUIDANCE);
|
||||
mNextLaunched = savedInstanceState.getBoolean(EXTRA_KEY_NEXT_LAUNCHED);
|
||||
}
|
||||
mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
|
||||
mPostureGuidanceIntent = FeatureFactory.getFactory(getApplicationContext())
|
||||
.getFaceFeatureProvider().getPostureGuidanceIntent(getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,6 +182,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
outState.putBoolean(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
|
||||
outState.putLong(EXTRA_KEY_CHALLENGE, mChallenge);
|
||||
outState.putInt(EXTRA_KEY_SENSOR_ID, mSensorId);
|
||||
outState.putBoolean(EXTRA_LAUNCHED_POSTURE_GUIDANCE, mLaunchedPostureGuidance);
|
||||
outState.putBoolean(EXTRA_KEY_NEXT_LAUNCHED, mNextLaunched);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,6 +209,12 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (mScreenSizeFoldProvider != null && mFoldCallback != null) {
|
||||
mScreenSizeFoldProvider.unregisterCallback(mFoldCallback);
|
||||
}
|
||||
mScreenSizeFoldProvider = null;
|
||||
mFoldCallback = null;
|
||||
|
||||
if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
|
||||
&& !BiometricUtils.isAnyMultiBiometricFlow(this)) {
|
||||
setResult(RESULT_TIMEOUT);
|
||||
@@ -191,6 +222,17 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean launchPostureGuidance() {
|
||||
if (mPostureGuidanceIntent == null || mLaunchedPostureGuidance) {
|
||||
return false;
|
||||
}
|
||||
BiometricUtils.copyMultiBiometricExtras(getIntent(), mPostureGuidanceIntent);
|
||||
startActivityForResult(mPostureGuidanceIntent, REQUEST_POSTURE_GUIDANCE);
|
||||
mLaunchedPostureGuidance = true;
|
||||
overridePendingTransition(0 /* no enter anim */, 0 /* no exit anim */);
|
||||
return mLaunchedPostureGuidance;
|
||||
}
|
||||
|
||||
protected boolean shouldFinishWhenBackgrounded() {
|
||||
return !WizardManagerHelper.isAnySetupWizard(getIntent());
|
||||
}
|
||||
|
@@ -155,6 +155,8 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
if (savedInstanceState != null) {
|
||||
mConfirmingCredentials = savedInstanceState.getBoolean(KEY_CONFIRMING_CREDENTIALS);
|
||||
mHasScrolledToBottom = savedInstanceState.getBoolean(KEY_SCROLLED_TO_BOTTOM);
|
||||
mLaunchedPostureGuidance = savedInstanceState.getBoolean(
|
||||
EXTRA_LAUNCHED_POSTURE_GUIDANCE);
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
@@ -273,6 +275,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
||||
finish();
|
||||
}
|
||||
}
|
||||
mNextLaunched = true;
|
||||
}
|
||||
|
||||
private void launchChooseLock() {
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.biometrics;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
@@ -47,17 +48,53 @@ import com.android.settings.password.SetupChooseLockGeneric;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Common biometric utilities.
|
||||
*/
|
||||
public class BiometricUtils {
|
||||
private static final String TAG = "BiometricUtils";
|
||||
|
||||
// Note: Theis IntDef must align SystemUI DevicePostureInt
|
||||
@IntDef(prefix = {"DEVICE_POSTURE_"}, value = {
|
||||
DEVICE_POSTURE_UNKNOWN,
|
||||
DEVICE_POSTURE_CLOSED,
|
||||
DEVICE_POSTURE_HALF_OPENED,
|
||||
DEVICE_POSTURE_OPENED,
|
||||
DEVICE_POSTURE_FLIPPED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DevicePostureInt {}
|
||||
|
||||
// NOTE: These constants **must** match those defined for Jetpack Sidecar. This is because we
|
||||
// use the Device State -> Jetpack Posture map in DevicePostureControllerImpl to translate
|
||||
// between the two.
|
||||
public static final int DEVICE_POSTURE_UNKNOWN = 0;
|
||||
public static final int DEVICE_POSTURE_CLOSED = 1;
|
||||
public static final int DEVICE_POSTURE_HALF_OPENED = 2;
|
||||
public static final int DEVICE_POSTURE_OPENED = 3;
|
||||
public static final int DEVICE_POSTURE_FLIPPED = 4;
|
||||
|
||||
public static int sAllowEnrollPosture = DEVICE_POSTURE_UNKNOWN;
|
||||
|
||||
/**
|
||||
* Request was sent for starting another enrollment of a previously
|
||||
* enrolled biometric of the same type.
|
||||
*/
|
||||
public static int REQUEST_ADD_ANOTHER = 7;
|
||||
|
||||
/**
|
||||
* Gatekeeper credential not match exception, it throws if VerifyCredentialResponse is not
|
||||
* matched in requestGatekeeperHat().
|
||||
*/
|
||||
public static class GatekeeperCredentialNotMatchException extends IllegalStateException {
|
||||
public GatekeeperCredentialNotMatchException(String s) {
|
||||
super(s);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given the result from confirming or choosing a credential, request Gatekeeper to generate
|
||||
* a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge.
|
||||
@@ -67,6 +104,8 @@ public class BiometricUtils {
|
||||
* @param userId User ID that the credential/biometric operation applies to
|
||||
* @param challenge Unique biometric challenge from FingerprintManager/FaceManager
|
||||
* @return
|
||||
* @throws GatekeeperCredentialNotMatchException if Gatekeeper response is not match
|
||||
* @throws IllegalStateException if Gatekeeper Password is missing
|
||||
*/
|
||||
public static byte[] requestGatekeeperHat(@NonNull Context context, @NonNull Intent result,
|
||||
int userId, long challenge) {
|
||||
@@ -84,7 +123,7 @@ public class BiometricUtils {
|
||||
final VerifyCredentialResponse response = utils.verifyGatekeeperPasswordHandle(gkPwHandle,
|
||||
challenge, userId);
|
||||
if (!response.isMatched()) {
|
||||
throw new IllegalStateException("Unable to request Gatekeeper HAT");
|
||||
throw new GatekeeperCredentialNotMatchException("Unable to request Gatekeeper HAT");
|
||||
}
|
||||
return response.getGatekeeperHAT();
|
||||
}
|
||||
@@ -270,6 +309,51 @@ public class BiometricUtils {
|
||||
|| isMultiBiometricFingerprintEnrollmentFlow(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if the activity is showing a posture guidance to user.
|
||||
*
|
||||
* @param devicePosture the device posture state
|
||||
* @param isLaunchedPostureGuidance True launching a posture guidance to user
|
||||
* @return True if the activity is showing posture guidance to user
|
||||
*/
|
||||
public static boolean isPostureGuidanceShowing(@DevicePostureInt int devicePosture,
|
||||
boolean isLaunchedPostureGuidance) {
|
||||
return !isPostureAllowEnrollment(devicePosture) && isLaunchedPostureGuidance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if current device posture state is allow to enroll biometrics.
|
||||
* For compatibility, we don't restrict enrollment if device do not config.
|
||||
*
|
||||
* @param devicePosture True if current device posture allow enrollment
|
||||
* @return True if current device posture state allow enrollment
|
||||
*/
|
||||
public static boolean isPostureAllowEnrollment(@DevicePostureInt int devicePosture) {
|
||||
return (sAllowEnrollPosture == DEVICE_POSTURE_UNKNOWN)
|
||||
|| (devicePosture == sAllowEnrollPosture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to check if the activity should show a posture guidance to user.
|
||||
*
|
||||
* @param devicePosture the device posture state
|
||||
* @param isLaunchedPostureGuidance True launching a posture guidance to user
|
||||
* @return True if posture disallow enroll and posture guidance not showing, false otherwise.
|
||||
*/
|
||||
public static boolean shouldShowPostureGuidance(@DevicePostureInt int devicePosture,
|
||||
boolean isLaunchedPostureGuidance) {
|
||||
return !isPostureAllowEnrollment(devicePosture) && !isLaunchedPostureGuidance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets allowed device posture for face enrollment.
|
||||
*
|
||||
* @param devicePosture the allowed posture state {@link DevicePostureInt} for enrollment
|
||||
*/
|
||||
public static void setDevicePosturesAllowEnroll(@DevicePostureInt int devicePosture) {
|
||||
sAllowEnrollPosture = devicePosture;
|
||||
}
|
||||
|
||||
public static void copyMultiBiometricExtras(@NonNull Intent fromIntent,
|
||||
@NonNull Intent toIntent) {
|
||||
PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
|
||||
|
@@ -16,24 +16,35 @@
|
||||
|
||||
package com.android.settings.biometrics.face;
|
||||
|
||||
import static com.android.settings.biometrics.BiometricUtils.isPostureAllowEnrollment;
|
||||
import static com.android.settings.biometrics.BiometricUtils.isPostureGuidanceShowing;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
import com.android.systemui.unfold.updates.FoldProvider;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
@@ -41,18 +52,19 @@ import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.view.IllustrationVideoView;
|
||||
|
||||
/**
|
||||
* Provides animated education for users to know how to enroll a face with appropriate posture.
|
||||
*/
|
||||
public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
private static final String TAG = "FaceEducation";
|
||||
|
||||
private FaceManager mFaceManager;
|
||||
private FaceEnrollAccessibilityToggle mSwitchDiversity;
|
||||
|
||||
private boolean mIsUsingLottie;
|
||||
private IllustrationVideoView mIllustrationDefault;
|
||||
private LottieAnimationView mIllustrationLottie;
|
||||
private View mIllustrationAccessibility;
|
||||
private Intent mResultIntent;
|
||||
private boolean mNextClicked;
|
||||
private boolean mAccessibilityEnabled;
|
||||
|
||||
private final CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
|
||||
@@ -154,6 +166,34 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (getPostureGuidanceIntent() == null) {
|
||||
Log.d(TAG, "Device do not support posture guidance");
|
||||
return;
|
||||
}
|
||||
|
||||
BiometricUtils.setDevicePosturesAllowEnroll(
|
||||
getResources().getInteger(R.integer.config_face_enroll_supported_posture));
|
||||
|
||||
if (getPostureCallback() == null) {
|
||||
mFoldCallback = isFolded -> {
|
||||
mDevicePostureState = isFolded ? BiometricUtils.DEVICE_POSTURE_CLOSED
|
||||
: BiometricUtils.DEVICE_POSTURE_OPENED;
|
||||
if (BiometricUtils.shouldShowPostureGuidance(mDevicePostureState,
|
||||
mLaunchedPostureGuidance) && !mNextLaunched) {
|
||||
launchPostureGuidance();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (mScreenSizeFoldProvider == null) {
|
||||
mScreenSizeFoldProvider = new ScreenSizeFoldProvider(getApplicationContext());
|
||||
mScreenSizeFoldProvider.registerCallback(mFoldCallback, getMainExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
@@ -172,7 +212,8 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
|
||||
@Override
|
||||
protected boolean shouldFinishWhenBackgrounded() {
|
||||
return super.shouldFinishWhenBackgrounded() && !mNextClicked;
|
||||
return super.shouldFinishWhenBackgrounded() && !mNextLaunched
|
||||
&& !isPostureGuidanceShowing(mDevicePostureState, mLaunchedPostureGuidance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -206,13 +247,14 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
|
||||
dialog.setPositiveButtonListener((dialog1, which) -> {
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
mNextClicked = true;
|
||||
mNextLaunched = true;
|
||||
});
|
||||
dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
|
||||
} else {
|
||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||
mNextClicked = true;
|
||||
mNextLaunched = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void onSkipButtonClick(View view) {
|
||||
@@ -223,15 +265,29 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (mScreenSizeFoldProvider != null && getPostureCallback() != null) {
|
||||
mScreenSizeFoldProvider.onConfigurationChange(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_POSTURE_GUIDANCE) {
|
||||
mLaunchedPostureGuidance = false;
|
||||
if (resultCode == RESULT_CANCELED || resultCode == RESULT_SKIP) {
|
||||
onSkipButtonClick(getCurrentFocus());
|
||||
}
|
||||
return;
|
||||
}
|
||||
mResultIntent = data;
|
||||
boolean hasEnrolledFace = false;
|
||||
if (data != null) {
|
||||
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
||||
}
|
||||
if (resultCode == RESULT_TIMEOUT) {
|
||||
if (resultCode == RESULT_TIMEOUT || !isPostureAllowEnrollment(mDevicePostureState)) {
|
||||
setResult(resultCode, data);
|
||||
finish();
|
||||
} else if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
||||
@@ -243,6 +299,26 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
mNextLaunched = false;
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected Intent getPostureGuidanceIntent() {
|
||||
return mPostureGuidanceIntent;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected FoldProvider.FoldCallback getPostureCallback() {
|
||||
return mFoldCallback;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@BiometricUtils.DevicePostureInt
|
||||
protected int getDevicePostureState() {
|
||||
return mDevicePostureState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,8 +338,10 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||
|
||||
private void showDefaultIllustration() {
|
||||
if (mIsUsingLottie) {
|
||||
mIllustrationLottie.setAnimation(R.raw.face_education_lottie);
|
||||
mIllustrationLottie.setVisibility(View.VISIBLE);
|
||||
mIllustrationLottie.playAnimation();
|
||||
mIllustrationLottie.setProgress(0f);
|
||||
} else {
|
||||
mIllustrationDefault.setVisibility(View.VISIBLE);
|
||||
mIllustrationDefault.start();
|
||||
|
@@ -18,9 +18,12 @@ package com.android.settings.biometrics.face;
|
||||
|
||||
import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_UNLOCK_DISABLED;
|
||||
|
||||
import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.SensorPrivacyManager;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.face.FaceManager;
|
||||
@@ -36,6 +39,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
@@ -43,11 +47,12 @@ import com.android.settings.biometrics.BiometricEnrollActivity;
|
||||
import com.android.settings.biometrics.BiometricEnrollIntroduction;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.biometrics.MultiBiometricEnrollHelper;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
import com.android.settings.utils.SensorPrivacyManagerHelper;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
import com.android.systemui.unfold.updates.FoldProvider;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
@@ -61,7 +66,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
private static final String TAG = "FaceEnrollIntroduction";
|
||||
|
||||
private FaceManager mFaceManager;
|
||||
private FaceFeatureProvider mFaceFeatureProvider;
|
||||
@Nullable private FooterButton mPrimaryFooterButton;
|
||||
@Nullable private FooterButton mSecondaryFooterButton;
|
||||
@Nullable private SensorPrivacyManager mSensorPrivacyManager;
|
||||
@@ -98,6 +102,12 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldFinishWhenBackgrounded() {
|
||||
return super.shouldFinishWhenBackgrounded() && !BiometricUtils.isPostureGuidanceShowing(
|
||||
mDevicePostureState, mLaunchedPostureGuidance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -142,9 +152,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
infoMessageRequireEyes.setText(getInfoMessageRequireEyes());
|
||||
}
|
||||
|
||||
mFaceManager = Utils.getFaceManagerOrNull(this);
|
||||
mFaceFeatureProvider = FeatureFactory.getFactory(getApplicationContext())
|
||||
.getFaceFeatureProvider();
|
||||
mFaceManager = getFaceManager();
|
||||
|
||||
// This path is an entry point for SetNewPasswordController, e.g.
|
||||
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
|
||||
@@ -154,11 +162,22 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
// We either block on generateChallenge, or need to gray out the "next" button until
|
||||
// the challenge is ready. Let's just do this for now.
|
||||
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
|
||||
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId,
|
||||
challenge);
|
||||
mSensorId = sensorId;
|
||||
mChallenge = challenge;
|
||||
mFooterBarMixin.getPrimaryButton().setEnabled(true);
|
||||
if (isFinishing()) {
|
||||
// Do nothing if activity is finishing
|
||||
Log.w(TAG, "activity finished before challenge callback launched.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mToken = requestGatekeeperHat(challenge);
|
||||
mSensorId = sensorId;
|
||||
mChallenge = challenge;
|
||||
mFooterBarMixin.getPrimaryButton().setEnabled(true);
|
||||
} catch (GatekeeperCredentialNotMatchException e) {
|
||||
// Let BiometricEnrollBase#onCreate() to trigger confirmLock()
|
||||
getIntent().removeExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE);
|
||||
recreate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -172,8 +191,82 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
Log.v(TAG, "cameraPrivacyEnabled : " + cameraPrivacyEnabled);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected FaceManager getFaceManager() {
|
||||
return Utils.getFaceManagerOrNull(this);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected Intent getPostureGuidanceIntent() {
|
||||
return mPostureGuidanceIntent;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected FoldProvider.FoldCallback getPostureCallback() {
|
||||
return mFoldCallback;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@BiometricUtils.DevicePostureInt
|
||||
protected int getDevicePostureState() {
|
||||
return mDevicePostureState;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
protected byte[] requestGatekeeperHat(long challenge) {
|
||||
return BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (mScreenSizeFoldProvider != null && getPostureCallback() != null) {
|
||||
mScreenSizeFoldProvider.onConfigurationChange(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (getPostureGuidanceIntent() == null) {
|
||||
Log.d(TAG, "Device do not support posture guidance");
|
||||
return;
|
||||
}
|
||||
|
||||
BiometricUtils.setDevicePosturesAllowEnroll(
|
||||
getResources().getInteger(R.integer.config_face_enroll_supported_posture));
|
||||
|
||||
if (getPostureCallback() == null) {
|
||||
mFoldCallback = isFolded -> {
|
||||
mDevicePostureState = isFolded ? BiometricUtils.DEVICE_POSTURE_CLOSED
|
||||
: BiometricUtils.DEVICE_POSTURE_OPENED;
|
||||
if (BiometricUtils.shouldShowPostureGuidance(mDevicePostureState,
|
||||
mLaunchedPostureGuidance) && !mNextLaunched) {
|
||||
launchPostureGuidance();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (mScreenSizeFoldProvider == null) {
|
||||
mScreenSizeFoldProvider = new ScreenSizeFoldProvider(getApplicationContext());
|
||||
mScreenSizeFoldProvider.registerCallback(mFoldCallback, getMainExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == REQUEST_POSTURE_GUIDANCE) {
|
||||
mLaunchedPostureGuidance = false;
|
||||
if (resultCode == RESULT_CANCELED || resultCode == RESULT_SKIP) {
|
||||
onSkipButtonClick(getCurrentFocus());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If user has skipped or finished enrolling, don't restart enrollment.
|
||||
final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
||||
|| requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST;
|
||||
@@ -184,10 +277,12 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
||||
}
|
||||
|
||||
if (resultCode == RESULT_CANCELED && hasEnrolledFace) {
|
||||
setResult(resultCode, data);
|
||||
finish();
|
||||
return;
|
||||
if (resultCode == RESULT_CANCELED) {
|
||||
if (hasEnrolledFace || !BiometricUtils.isPostureAllowEnrollment(mDevicePostureState)) {
|
||||
setResult(resultCode, data);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnrollRequest && isResultSkipOrFinished || hasEnrolledFace) {
|
||||
|
@@ -17,9 +17,19 @@
|
||||
package com.android.settings.biometrics.face;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/** Feature provider for face unlock */
|
||||
public interface FaceFeatureProvider {
|
||||
/** Returns specified intent config by resource R.string.config_face_enroll_guidance_page. */
|
||||
@Nullable
|
||||
Intent getPostureGuidanceIntent(Context context);
|
||||
|
||||
/** Returns true if attention checking is supported. */
|
||||
boolean isAttentionSupported(Context context);
|
||||
|
||||
/** Returns true if setup wizard supported face enrollment. */
|
||||
boolean isSetupWizardSupported(Context context);
|
||||
}
|
||||
|
@@ -16,13 +16,48 @@
|
||||
|
||||
package com.android.settings.biometrics.face;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
public class FaceFeatureProviderImpl implements FaceFeatureProvider {
|
||||
|
||||
/**
|
||||
* Returns the guidance page intent if device support {@link FoldingFeature}, and we want to
|
||||
* guide user enrolling faces with specific device posture.
|
||||
*
|
||||
* @param context the application context
|
||||
* @return the posture guidance intent, otherwise null if device not support
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Intent getPostureGuidanceIntent(Context context) {
|
||||
final String flattenedString = context.getString(R.string.config_face_enroll_guidance_page);
|
||||
final Intent intent;
|
||||
if (!TextUtils.isEmpty(flattenedString)) {
|
||||
ComponentName componentName = ComponentName.unflattenFromString(flattenedString);
|
||||
if (componentName != null) {
|
||||
intent = new Intent();
|
||||
intent.setComponent(componentName);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttentionSupported(Context context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSetupWizardSupported(@NonNull Context context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user