Merge changes from topics "disable_face_enroll_inSUW", "tuscany_FaceEnrollFoldPage" into tm-qpr-dev am: 34d144265d
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/20673972 Change-Id: I1b5fc399539a8f6585588c90a8481e0a8c4d5463 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
0
res/raw/face_posture_guidance_lottie.json
Normal file
0
res/raw/face_posture_guidance_lottie.json
Normal file
@@ -283,6 +283,19 @@
|
|||||||
<!-- ComponentName to launch a vendor-specific enrollment activity if available -->
|
<!-- ComponentName to launch a vendor-specific enrollment activity if available -->
|
||||||
<string name="config_face_enroll" translatable="false"></string>
|
<string name="config_face_enroll" translatable="false"></string>
|
||||||
|
|
||||||
|
<!-- ComponentName to launch a vendor-specific posture guidance activity if available -->
|
||||||
|
<string name="config_face_enroll_guidance_page" translatable="false"></string>
|
||||||
|
|
||||||
|
<!-- Whether to support posture listening for face auth, default is 0(DEVICE_POSTURE_UNKNOWN)
|
||||||
|
means setting will try listening on device posture changes.
|
||||||
|
0 : DEVICE_POSTURE_UNKNOWN
|
||||||
|
1 : DEVICE_POSTURE_CLOSED
|
||||||
|
2 : DEVICE_POSTURE_HALF_OPENED
|
||||||
|
3 : DEVICE_POSTURE_OPENED
|
||||||
|
4 : DEVICE_POSTURE_FLIPPED
|
||||||
|
-->
|
||||||
|
<integer name="config_face_enroll_supported_posture">0</integer>
|
||||||
|
|
||||||
<!-- Whether to show the "less secure" info section on the face enroll intro screen -->
|
<!-- Whether to show the "less secure" info section on the face enroll intro screen -->
|
||||||
<bool name="config_face_intro_show_less_secure">false</bool>
|
<bool name="config_face_intro_show_less_secure">false</bool>
|
||||||
|
|
||||||
|
@@ -36,6 +36,7 @@ import androidx.window.embedding.SplitRule;
|
|||||||
import com.android.settings.Settings;
|
import com.android.settings.Settings;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.SubSettings;
|
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.FingerprintEnrollEnrolling;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal;
|
||||||
@@ -228,6 +229,8 @@ public class ActivityEmbeddingRulesController {
|
|||||||
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
|
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
|
||||||
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
|
addActivityFilter(activityFilters, FingerprintEnrollIntroductionInternal.class);
|
||||||
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
|
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
|
||||||
|
addActivityFilter(activityFilters, FaceEnrollIntroductionInternal.class);
|
||||||
|
addActivityFilter(activityFilters, Settings.FaceSettingsInternalActivity.class);
|
||||||
addActivityFilter(activityFilters, AvatarPickerActivity.class);
|
addActivityFilter(activityFilters, AvatarPickerActivity.class);
|
||||||
mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */));
|
mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */));
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,10 @@ import com.android.settings.SetupWizardUtils;
|
|||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||||
import com.android.settings.core.InstrumentedActivity;
|
import com.android.settings.core.InstrumentedActivity;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
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.FooterBarMixin;
|
||||||
import com.google.android.setupcompat.template.FooterButton;
|
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_SENSOR_ID = "sensor_id";
|
||||||
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
public static final String EXTRA_KEY_CHALLENGE = "challenge";
|
||||||
public static final String EXTRA_KEY_MODALITY = "sensor_modality";
|
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_FACE = "finished_enrolling_face";
|
||||||
public static final String EXTRA_FINISHED_ENROLL_FINGERPRINT = "finished_enrolling_fingerprint";
|
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
|
* 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.
|
* example, when starting fingerprint enroll after face enroll.
|
||||||
*/
|
*/
|
||||||
public static final int ENROLL_NEXT_BIOMETRIC_REQUEST = 6;
|
public static final int ENROLL_NEXT_BIOMETRIC_REQUEST = 6;
|
||||||
|
public static final int REQUEST_POSTURE_GUIDANCE = 7;
|
||||||
|
|
||||||
protected boolean mLaunchedConfirmLock;
|
protected boolean mLaunchedConfirmLock;
|
||||||
|
protected boolean mLaunchedPostureGuidance;
|
||||||
|
protected boolean mNextLaunched;
|
||||||
protected byte[] mToken;
|
protected byte[] mToken;
|
||||||
protected int mUserId;
|
protected int mUserId;
|
||||||
protected int mSensorId;
|
protected int mSensorId;
|
||||||
|
@BiometricUtils.DevicePostureInt
|
||||||
|
protected int mDevicePostureState;
|
||||||
protected long mChallenge;
|
protected long mChallenge;
|
||||||
protected boolean mFromSettingsSummary;
|
protected boolean mFromSettingsSummary;
|
||||||
protected FooterBarMixin mFooterBarMixin;
|
protected FooterBarMixin mFooterBarMixin;
|
||||||
|
@Nullable
|
||||||
|
protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
|
||||||
|
@Nullable
|
||||||
|
protected Intent mPostureGuidanceIntent = null;
|
||||||
|
@Nullable
|
||||||
|
protected FoldProvider.FoldCallback mFoldCallback = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -139,16 +155,23 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||||
}
|
}
|
||||||
mFromSettingsSummary = getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
mFromSettingsSummary = getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
||||||
if (savedInstanceState != null && mToken == null) {
|
if (savedInstanceState != null) {
|
||||||
mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
|
if (mToken == null) {
|
||||||
mToken = savedInstanceState.getByteArray(
|
mLaunchedConfirmLock = savedInstanceState.getBoolean(EXTRA_KEY_LAUNCHED_CONFIRM);
|
||||||
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
mToken = savedInstanceState.getByteArray(
|
||||||
mFromSettingsSummary =
|
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
|
||||||
savedInstanceState.getBoolean(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
mFromSettingsSummary =
|
||||||
mChallenge = savedInstanceState.getLong(EXTRA_KEY_CHALLENGE);
|
savedInstanceState.getBoolean(EXTRA_FROM_SETTINGS_SUMMARY, false);
|
||||||
mSensorId = savedInstanceState.getInt(EXTRA_KEY_SENSOR_ID);
|
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());
|
mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
|
||||||
|
mPostureGuidanceIntent = FeatureFactory.getFactory(getApplicationContext())
|
||||||
|
.getFaceFeatureProvider().getPostureGuidanceIntent(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -159,6 +182,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
outState.putBoolean(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
|
outState.putBoolean(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
|
||||||
outState.putLong(EXTRA_KEY_CHALLENGE, mChallenge);
|
outState.putLong(EXTRA_KEY_CHALLENGE, mChallenge);
|
||||||
outState.putInt(EXTRA_KEY_SENSOR_ID, mSensorId);
|
outState.putInt(EXTRA_KEY_SENSOR_ID, mSensorId);
|
||||||
|
outState.putBoolean(EXTRA_LAUNCHED_POSTURE_GUIDANCE, mLaunchedPostureGuidance);
|
||||||
|
outState.putBoolean(EXTRA_KEY_NEXT_LAUNCHED, mNextLaunched);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -184,6 +209,12 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
if (mScreenSizeFoldProvider != null && mFoldCallback != null) {
|
||||||
|
mScreenSizeFoldProvider.unregisterCallback(mFoldCallback);
|
||||||
|
}
|
||||||
|
mScreenSizeFoldProvider = null;
|
||||||
|
mFoldCallback = null;
|
||||||
|
|
||||||
if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
|
if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
|
||||||
&& !BiometricUtils.isAnyMultiBiometricFlow(this)) {
|
&& !BiometricUtils.isAnyMultiBiometricFlow(this)) {
|
||||||
setResult(RESULT_TIMEOUT);
|
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() {
|
protected boolean shouldFinishWhenBackgrounded() {
|
||||||
return !WizardManagerHelper.isAnySetupWizard(getIntent());
|
return !WizardManagerHelper.isAnySetupWizard(getIntent());
|
||||||
}
|
}
|
||||||
|
@@ -155,6 +155,8 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
|||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
mConfirmingCredentials = savedInstanceState.getBoolean(KEY_CONFIRMING_CREDENTIALS);
|
mConfirmingCredentials = savedInstanceState.getBoolean(KEY_CONFIRMING_CREDENTIALS);
|
||||||
mHasScrolledToBottom = savedInstanceState.getBoolean(KEY_SCROLLED_TO_BOTTOM);
|
mHasScrolledToBottom = savedInstanceState.getBoolean(KEY_SCROLLED_TO_BOTTOM);
|
||||||
|
mLaunchedPostureGuidance = savedInstanceState.getBoolean(
|
||||||
|
EXTRA_LAUNCHED_POSTURE_GUIDANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
@@ -273,6 +275,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mNextLaunched = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchChooseLock() {
|
private void launchChooseLock() {
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics;
|
package com.android.settings.biometrics;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
@@ -47,12 +48,37 @@ import com.android.settings.password.SetupChooseLockGeneric;
|
|||||||
|
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common biometric utilities.
|
* Common biometric utilities.
|
||||||
*/
|
*/
|
||||||
public class BiometricUtils {
|
public class BiometricUtils {
|
||||||
private static final String TAG = "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
|
* Request was sent for starting another enrollment of a previously
|
||||||
* enrolled biometric of the same type.
|
* enrolled biometric of the same type.
|
||||||
@@ -283,6 +309,51 @@ public class BiometricUtils {
|
|||||||
|| isMultiBiometricFingerprintEnrollmentFlow(activity);
|
|| 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,
|
public static void copyMultiBiometricExtras(@NonNull Intent fromIntent,
|
||||||
@NonNull Intent toIntent) {
|
@NonNull Intent toIntent) {
|
||||||
PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
|
PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra(
|
||||||
|
@@ -16,24 +16,35 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.face;
|
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.app.settings.SettingsEnums;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.hardware.face.FaceManager;
|
import android.hardware.face.FaceManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CompoundButton;
|
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.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||||
import com.android.settings.biometrics.BiometricUtils;
|
import com.android.settings.biometrics.BiometricUtils;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
import com.android.settings.password.SetupSkipDialog;
|
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.airbnb.lottie.LottieAnimationView;
|
||||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
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.setupcompat.util.WizardManagerHelper;
|
||||||
import com.google.android.setupdesign.view.IllustrationVideoView;
|
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 {
|
public class FaceEnrollEducation extends BiometricEnrollBase {
|
||||||
private static final String TAG = "FaceEducation";
|
private static final String TAG = "FaceEducation";
|
||||||
|
|
||||||
private FaceManager mFaceManager;
|
private FaceManager mFaceManager;
|
||||||
private FaceEnrollAccessibilityToggle mSwitchDiversity;
|
private FaceEnrollAccessibilityToggle mSwitchDiversity;
|
||||||
|
|
||||||
private boolean mIsUsingLottie;
|
private boolean mIsUsingLottie;
|
||||||
private IllustrationVideoView mIllustrationDefault;
|
private IllustrationVideoView mIllustrationDefault;
|
||||||
private LottieAnimationView mIllustrationLottie;
|
private LottieAnimationView mIllustrationLottie;
|
||||||
private View mIllustrationAccessibility;
|
private View mIllustrationAccessibility;
|
||||||
private Intent mResultIntent;
|
private Intent mResultIntent;
|
||||||
private boolean mNextClicked;
|
|
||||||
private boolean mAccessibilityEnabled;
|
private boolean mAccessibilityEnabled;
|
||||||
|
|
||||||
private final CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
|
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
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@@ -172,7 +212,8 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldFinishWhenBackgrounded() {
|
protected boolean shouldFinishWhenBackgrounded() {
|
||||||
return super.shouldFinishWhenBackgrounded() && !mNextClicked;
|
return super.shouldFinishWhenBackgrounded() && !mNextLaunched
|
||||||
|
&& !isPostureGuidanceShowing(mDevicePostureState, mLaunchedPostureGuidance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -206,13 +247,14 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
|||||||
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
|
FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
|
||||||
dialog.setPositiveButtonListener((dialog1, which) -> {
|
dialog.setPositiveButtonListener((dialog1, which) -> {
|
||||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||||
mNextClicked = true;
|
mNextLaunched = true;
|
||||||
});
|
});
|
||||||
dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
|
dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
|
||||||
} else {
|
} else {
|
||||||
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
|
||||||
mNextClicked = true;
|
mNextLaunched = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onSkipButtonClick(View view) {
|
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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
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;
|
mResultIntent = data;
|
||||||
boolean hasEnrolledFace = false;
|
boolean hasEnrolledFace = false;
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
||||||
}
|
}
|
||||||
if (resultCode == RESULT_TIMEOUT) {
|
if (resultCode == RESULT_TIMEOUT || !isPostureAllowEnrollment(mDevicePostureState)) {
|
||||||
setResult(resultCode, data);
|
setResult(resultCode, data);
|
||||||
finish();
|
finish();
|
||||||
} else if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
} else if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
||||||
@@ -243,6 +299,26 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
|||||||
finish();
|
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
|
@Override
|
||||||
@@ -262,8 +338,10 @@ public class FaceEnrollEducation extends BiometricEnrollBase {
|
|||||||
|
|
||||||
private void showDefaultIllustration() {
|
private void showDefaultIllustration() {
|
||||||
if (mIsUsingLottie) {
|
if (mIsUsingLottie) {
|
||||||
|
mIllustrationLottie.setAnimation(R.raw.face_education_lottie);
|
||||||
mIllustrationLottie.setVisibility(View.VISIBLE);
|
mIllustrationLottie.setVisibility(View.VISIBLE);
|
||||||
mIllustrationLottie.playAnimation();
|
mIllustrationLottie.playAnimation();
|
||||||
|
mIllustrationLottie.setProgress(0f);
|
||||||
} else {
|
} else {
|
||||||
mIllustrationDefault.setVisibility(View.VISIBLE);
|
mIllustrationDefault.setVisibility(View.VISIBLE);
|
||||||
mIllustrationDefault.start();
|
mIllustrationDefault.start();
|
||||||
|
@@ -23,6 +23,7 @@ import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentia
|
|||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.hardware.SensorPrivacyManager;
|
import android.hardware.SensorPrivacyManager;
|
||||||
import android.hardware.biometrics.BiometricAuthenticator;
|
import android.hardware.biometrics.BiometricAuthenticator;
|
||||||
import android.hardware.face.FaceManager;
|
import android.hardware.face.FaceManager;
|
||||||
@@ -50,6 +51,8 @@ import com.android.settings.password.ChooseLockSettingsHelper;
|
|||||||
import com.android.settings.password.SetupSkipDialog;
|
import com.android.settings.password.SetupSkipDialog;
|
||||||
import com.android.settings.utils.SensorPrivacyManagerHelper;
|
import com.android.settings.utils.SensorPrivacyManagerHelper;
|
||||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
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.template.FooterButton;
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
@@ -99,6 +102,12 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldFinishWhenBackgrounded() {
|
||||||
|
return super.shouldFinishWhenBackgrounded() && !BiometricUtils.isPostureGuidanceShowing(
|
||||||
|
mDevicePostureState, mLaunchedPostureGuidance);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -188,14 +197,76 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
|||||||
return Utils.getFaceManagerOrNull(this);
|
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
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
protected byte[] requestGatekeeperHat(long challenge) {
|
protected byte[] requestGatekeeperHat(long challenge) {
|
||||||
return BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, 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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
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.
|
// If user has skipped or finished enrolling, don't restart enrollment.
|
||||||
final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST
|
||||||
|| requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST;
|
|| requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST;
|
||||||
@@ -206,10 +277,12 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
|||||||
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultCode == RESULT_CANCELED && hasEnrolledFace) {
|
if (resultCode == RESULT_CANCELED) {
|
||||||
setResult(resultCode, data);
|
if (hasEnrolledFace || !BiometricUtils.isPostureAllowEnrollment(mDevicePostureState)) {
|
||||||
finish();
|
setResult(resultCode, data);
|
||||||
return;
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEnrollRequest && isResultSkipOrFinished || hasEnrolledFace) {
|
if (isEnrollRequest && isResultSkipOrFinished || hasEnrolledFace) {
|
||||||
|
@@ -17,9 +17,16 @@
|
|||||||
package com.android.settings.biometrics.face;
|
package com.android.settings.biometrics.face;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/** Feature provider for face unlock */
|
/** Feature provider for face unlock */
|
||||||
public interface FaceFeatureProvider {
|
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. */
|
/** Returns true if attention checking is supported. */
|
||||||
boolean isAttentionSupported(Context context);
|
boolean isAttentionSupported(Context context);
|
||||||
|
|
||||||
|
@@ -16,12 +16,41 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.face;
|
package com.android.settings.biometrics.face;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
public class FaceFeatureProviderImpl implements FaceFeatureProvider {
|
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
|
@Override
|
||||||
public boolean isAttentionSupported(Context context) {
|
public boolean isAttentionSupported(Context context) {
|
||||||
return true;
|
return true;
|
||||||
|
@@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.biometrics.face;
|
||||||
|
|
||||||
|
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
|
||||||
|
import static android.util.DisplayMetrics.DENSITY_XXXHIGH;
|
||||||
|
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_NEXT_LAUNCHED;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_LAUNCHED_POSTURE_GUIDANCE;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_CLOSED;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_OPENED;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_UNKNOWN;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.hardware.face.FaceManager;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||||
|
import com.google.android.setupcompat.template.FooterButton;
|
||||||
|
import com.google.android.setupdesign.GlifLayout;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.android.controller.ActivityController;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowUtils.class})
|
||||||
|
public class FaceEnrollEducationTest {
|
||||||
|
@Mock
|
||||||
|
private FaceManager mFaceManager;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private ActivityController<TestFaceEnrollEducation> mActivityController;
|
||||||
|
private TestFaceEnrollEducation mActivity;
|
||||||
|
private FakeFeatureFactory mFakeFeatureFactory;
|
||||||
|
|
||||||
|
public static class TestFaceEnrollEducation extends FaceEnrollEducation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean launchPostureGuidance() {
|
||||||
|
return super.launchPostureGuidance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
ShadowUtils.setFaceManager(mFaceManager);
|
||||||
|
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActivityForPosture() {
|
||||||
|
final Intent testIntent = new Intent();
|
||||||
|
// Set the challenge token so the confirm screen will not be shown
|
||||||
|
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
|
||||||
|
testIntent.putExtra(EXTRA_KEY_NEXT_LAUNCHED, false);
|
||||||
|
testIntent.putExtra(EXTRA_LAUNCHED_POSTURE_GUIDANCE, false);
|
||||||
|
|
||||||
|
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
|
||||||
|
testIntent);
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
mActivityController = Robolectric.buildActivity(
|
||||||
|
TestFaceEnrollEducation.class, testIntent);
|
||||||
|
mActivity = spy(mActivityController.create().get());
|
||||||
|
|
||||||
|
when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActivity() {
|
||||||
|
final Intent testIntent = new Intent();
|
||||||
|
// Set the challenge token so the confirm screen will not be shown
|
||||||
|
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
|
||||||
|
|
||||||
|
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
|
||||||
|
null /* Simulate no posture intent */);
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
mActivityController = Robolectric.buildActivity(
|
||||||
|
TestFaceEnrollEducation.class, testIntent);
|
||||||
|
mActivity = spy(mActivityController.create().get());
|
||||||
|
|
||||||
|
when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlifLayout getGlifLayout() {
|
||||||
|
return mActivity.findViewById(R.id.setup_wizard_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_hasHeader() {
|
||||||
|
setupActivity();
|
||||||
|
CharSequence headerText = getGlifLayout().getHeaderText();
|
||||||
|
|
||||||
|
assertThat(headerText.toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.security_settings_face_enroll_education_title));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_hasDescription() {
|
||||||
|
setupActivity();
|
||||||
|
CharSequence desc = getGlifLayout().getDescriptionText();
|
||||||
|
|
||||||
|
assertThat(desc.toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.security_settings_face_enroll_education_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_showFooterPrimaryButton() {
|
||||||
|
setupActivity();
|
||||||
|
FooterBarMixin footer = getGlifLayout().getMixin(FooterBarMixin.class);
|
||||||
|
FooterButton footerButton = footer.getPrimaryButton();
|
||||||
|
|
||||||
|
assertThat(footerButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(footerButton.getText().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.security_settings_face_enroll_education_start));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_showFooterSecondaryButton() {
|
||||||
|
setupActivity();
|
||||||
|
FooterBarMixin footer = getGlifLayout().getMixin(FooterBarMixin.class);
|
||||||
|
FooterButton footerButton = footer.getSecondaryButton();
|
||||||
|
|
||||||
|
assertThat(footerButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(footerButton.getText().toString()).isEqualTo(mContext.getString(
|
||||||
|
R.string.security_settings_face_enroll_introduction_cancel));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_defaultNeverLaunchPostureGuidance() {
|
||||||
|
setupActivity();
|
||||||
|
|
||||||
|
assertThat(mActivity.launchPostureGuidance()).isFalse();
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_onStartNeverRegisterPostureChangeCallback() {
|
||||||
|
setupActivity();
|
||||||
|
mActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mActivity.getPostureGuidanceIntent()).isNull();
|
||||||
|
assertThat(mActivity.getPostureCallback()).isNull();
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducationWithPosture_onStartRegisteredPostureChangeCallback() {
|
||||||
|
setupActivityForPosture();
|
||||||
|
mActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mActivity.getPostureCallback()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducationWithPosture_onFoldedUpdated_unFolded() {
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.smallestScreenWidthDp = DENSITY_XXXHIGH;
|
||||||
|
setupActivityForPosture();
|
||||||
|
mActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mActivity.getPostureCallback()).isNotNull();
|
||||||
|
|
||||||
|
mActivity.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_OPENED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducationWithPosture_onFoldedUpdated_folded() {
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.smallestScreenWidthDp = DENSITY_DEFAULT;
|
||||||
|
setupActivityForPosture();
|
||||||
|
mActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mActivity.getPostureCallback()).isNotNull();
|
||||||
|
|
||||||
|
mActivity.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_CLOSED);
|
||||||
|
}
|
||||||
|
}
|
@@ -16,29 +16,61 @@
|
|||||||
|
|
||||||
package com.android.settings.biometrics.face;
|
package com.android.settings.biometrics.face;
|
||||||
|
|
||||||
|
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||||
|
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
|
||||||
|
import static android.util.DisplayMetrics.DENSITY_XXXHIGH;
|
||||||
|
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_NEXT_LAUNCHED;
|
||||||
|
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_LAUNCHED_POSTURE_GUIDANCE;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_CLOSED;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_OPENED;
|
||||||
|
import static com.android.settings.biometrics.BiometricUtils.DEVICE_POSTURE_UNKNOWN;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.hardware.face.Face;
|
||||||
import android.hardware.face.FaceManager;
|
import android.hardware.face.FaceManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.settings.R;
|
||||||
import com.android.settings.biometrics.BiometricUtils;
|
import com.android.settings.biometrics.BiometricUtils;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||||
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
|
||||||
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
|
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
|
||||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||||
|
import com.google.android.setupcompat.template.FooterButton;
|
||||||
|
import com.google.android.setupdesign.GlifLayout;
|
||||||
|
import com.google.android.setupdesign.view.BottomScrollView;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
@@ -47,69 +79,30 @@ import org.robolectric.android.controller.ActivityController;
|
|||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.ShadowActivity;
|
import org.robolectric.shadows.ShadowActivity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(shadows = {
|
@Config(shadows = {
|
||||||
ShadowLockPatternUtils.class,
|
ShadowLockPatternUtils.class,
|
||||||
ShadowUserManager.class,
|
ShadowUserManager.class,
|
||||||
|
ShadowUtils.class,
|
||||||
|
ShadowDevicePolicyManager.class,
|
||||||
ShadowSensorPrivacyManager.class
|
ShadowSensorPrivacyManager.class
|
||||||
})
|
})
|
||||||
public class FaceEnrollIntroductionTest {
|
public class FaceEnrollIntroductionTest {
|
||||||
|
|
||||||
@Mock private FaceManager mFaceManager;
|
@Mock
|
||||||
|
private FaceManager mFaceManager;
|
||||||
|
@Mock
|
||||||
|
private LockPatternUtils mLockPatternUtils;
|
||||||
|
|
||||||
private ActivityController<TestFaceEnrollIntroduction> mController;
|
private Context mContext;
|
||||||
|
private ActivityController<? extends Activity> mController;
|
||||||
private TestFaceEnrollIntroduction mActivity;
|
private TestFaceEnrollIntroduction mActivity;
|
||||||
|
private FaceEnrollIntroduction mSpyActivity;
|
||||||
@Before
|
private FakeFeatureFactory mFakeFeatureFactory;
|
||||||
public void setUp() {
|
private ShadowUserManager mUserManager;
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupActivity(@NonNull Intent intent) {
|
|
||||||
doAnswer(invocation -> {
|
|
||||||
final FaceManager.GenerateChallengeCallback callback =
|
|
||||||
invocation.getArgument(1);
|
|
||||||
callback.onGenerateChallengeResult(0, 0, 1L);
|
|
||||||
return null;
|
|
||||||
}).when(mFaceManager).generateChallenge(anyInt(), any());
|
|
||||||
mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent);
|
|
||||||
mActivity = mController.get();
|
|
||||||
mActivity.mOverrideFaceManager = mFaceManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnCreate() {
|
|
||||||
setupActivity(new Intent());
|
|
||||||
mController.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnCreateToGenerateChallenge() {
|
|
||||||
setupActivity(new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
|
|
||||||
mActivity.mGateKeeperAction = GateKeeperAction.RETURN_BYTE_ARRAY;
|
|
||||||
mController.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGenerateChallengeFailThenRecreate() {
|
|
||||||
setupActivity(new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
|
|
||||||
mActivity.mGateKeeperAction = GateKeeperAction.THROW_CREDENTIAL_NOT_MATCH;
|
|
||||||
mController.create();
|
|
||||||
|
|
||||||
// Make sure recreate() is called on original activity
|
|
||||||
assertThat(mActivity.getRecreateCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
// Simulate recreate() action
|
|
||||||
setupActivity(mActivity.getIntent());
|
|
||||||
mController.create();
|
|
||||||
|
|
||||||
// Verify confirmLock()
|
|
||||||
assertThat(mActivity.getConfirmingCredentials()).isTrue();
|
|
||||||
ShadowActivity shadowActivity = Shadows.shadowOf(mActivity);
|
|
||||||
ShadowActivity.IntentForResult startedActivity =
|
|
||||||
shadowActivity.getNextStartedActivityForResult();
|
|
||||||
assertWithMessage("Next activity 1").that(startedActivity).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum GateKeeperAction { CALL_SUPER, RETURN_BYTE_ARRAY, THROW_CREDENTIAL_NOT_MATCH }
|
enum GateKeeperAction { CALL_SUPER, RETURN_BYTE_ARRAY, THROW_CREDENTIAL_NOT_MATCH }
|
||||||
|
|
||||||
@@ -132,14 +125,15 @@ public class FaceEnrollIntroductionTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FaceManager mOverrideFaceManager = null;
|
public FaceManager mOverrideFaceManager = null;
|
||||||
@NonNull public GateKeeperAction mGateKeeperAction = GateKeeperAction.CALL_SUPER;
|
@NonNull
|
||||||
|
public GateKeeperAction mGateKeeperAction = GateKeeperAction.CALL_SUPER;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public byte[] requestGatekeeperHat(long challenge) {
|
public byte[] requestGatekeeperHat(long challenge) {
|
||||||
switch (mGateKeeperAction) {
|
switch (mGateKeeperAction) {
|
||||||
case RETURN_BYTE_ARRAY:
|
case RETURN_BYTE_ARRAY:
|
||||||
return new byte[] { 1 };
|
return new byte[]{1};
|
||||||
case THROW_CREDENTIAL_NOT_MATCH:
|
case THROW_CREDENTIAL_NOT_MATCH:
|
||||||
throw new BiometricUtils.GatekeeperCredentialNotMatchException("test");
|
throw new BiometricUtils.GatekeeperCredentialNotMatchException("test");
|
||||||
case CALL_SUPER:
|
case CALL_SUPER:
|
||||||
@@ -153,5 +147,257 @@ public class FaceEnrollIntroductionTest {
|
|||||||
protected FaceManager getFaceManager() {
|
protected FaceManager getFaceManager() {
|
||||||
return mOverrideFaceManager;
|
return mOverrideFaceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean launchPostureGuidance() {
|
||||||
|
return super.launchPostureGuidance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
ShadowUtils.setFaceManager(mFaceManager);
|
||||||
|
mUserManager = ShadowUserManager.getShadow();
|
||||||
|
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
|
||||||
|
when(mFakeFeatureFactory.securityFeatureProvider.getLockPatternUtils(any(Context.class)))
|
||||||
|
.thenReturn(mLockPatternUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActivity() {
|
||||||
|
final Intent testIntent = new Intent();
|
||||||
|
// Set the challenge token so the confirm screen will not be shown
|
||||||
|
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
|
||||||
|
|
||||||
|
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
|
||||||
|
null /* Simulate no posture intent */);
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
mUserManager.addUserProfile(new UserHandle(0));
|
||||||
|
mController = Robolectric.buildActivity(
|
||||||
|
TestFaceEnrollIntroduction.class, testIntent);
|
||||||
|
mActivity = (TestFaceEnrollIntroduction) spy(mController.get());
|
||||||
|
mActivity.mOverrideFaceManager = mFaceManager;
|
||||||
|
when(mActivity.getPostureGuidanceIntent()).thenReturn(null);
|
||||||
|
when(mContext.getApplicationContext()).thenReturn(mContext);
|
||||||
|
when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||||
|
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||||
|
when(mLockPatternUtils.getActivePasswordQuality(Mockito.anyInt())).thenReturn(
|
||||||
|
PASSWORD_QUALITY_NUMERIC);
|
||||||
|
|
||||||
|
mController.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActivityForPosture() {
|
||||||
|
final Intent testIntent = new Intent();
|
||||||
|
// Set the challenge token so the confirm screen will not be shown
|
||||||
|
testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
|
||||||
|
testIntent.putExtra(EXTRA_KEY_NEXT_LAUNCHED, false);
|
||||||
|
testIntent.putExtra(EXTRA_LAUNCHED_POSTURE_GUIDANCE, false);
|
||||||
|
|
||||||
|
when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn(
|
||||||
|
testIntent);
|
||||||
|
|
||||||
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
mUserManager.addUserProfile(new UserHandle(0));
|
||||||
|
mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, testIntent);
|
||||||
|
mSpyActivity = (FaceEnrollIntroduction) spy(mController.get());
|
||||||
|
when(mSpyActivity.getPostureGuidanceIntent()).thenReturn(testIntent);
|
||||||
|
when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||||
|
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||||
|
when(mLockPatternUtils.getActivePasswordQuality(Mockito.anyInt())).thenReturn(
|
||||||
|
PASSWORD_QUALITY_NUMERIC);
|
||||||
|
|
||||||
|
mController.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupActivityWithGenerateChallenge(@NonNull Intent intent) {
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
final FaceManager.GenerateChallengeCallback callback =
|
||||||
|
invocation.getArgument(1);
|
||||||
|
callback.onGenerateChallengeResult(0, 0, 1L);
|
||||||
|
return null;
|
||||||
|
}).when(mFaceManager).generateChallenge(anyInt(), any());
|
||||||
|
mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent);
|
||||||
|
mActivity = (TestFaceEnrollIntroduction) mController.get();
|
||||||
|
mActivity.mOverrideFaceManager = mFaceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlifLayout getGlifLayout(Activity activity) {
|
||||||
|
return activity.findViewById(R.id.setup_wizard_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFaceManagerToHave(int numEnrollments) {
|
||||||
|
List<Face> faces = new ArrayList<>();
|
||||||
|
for (int i = 0; i < numEnrollments; i++) {
|
||||||
|
faces.add(new Face("Face " + i /* name */, 1 /*faceId */, 1 /* deviceId */));
|
||||||
|
}
|
||||||
|
when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void intro_CheckCanEnroll() {
|
||||||
|
setFaceManagerToHave(0 /* numEnrollments */);
|
||||||
|
setupActivityWithGenerateChallenge(new Intent());
|
||||||
|
mController.create();
|
||||||
|
int result = mActivity.checkMaxEnrolled();
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void intro_CheckMaxEnrolled() {
|
||||||
|
setFaceManagerToHave(1 /* numEnrollments */);
|
||||||
|
setupActivityWithGenerateChallenge(new Intent());
|
||||||
|
mController.create();
|
||||||
|
int result = mActivity.checkMaxEnrolled();
|
||||||
|
|
||||||
|
assertThat(result).isEqualTo(R.string.face_intro_error_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnCreate() {
|
||||||
|
setupActivityWithGenerateChallenge(new Intent());
|
||||||
|
mController.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnCreateToGenerateChallenge() {
|
||||||
|
setupActivityWithGenerateChallenge(
|
||||||
|
new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
|
||||||
|
mActivity.mGateKeeperAction = GateKeeperAction.RETURN_BYTE_ARRAY;
|
||||||
|
mController.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateChallengeFailThenRecreate() {
|
||||||
|
setupActivityWithGenerateChallenge(
|
||||||
|
new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
|
||||||
|
mActivity.mGateKeeperAction = GateKeeperAction.THROW_CREDENTIAL_NOT_MATCH;
|
||||||
|
mController.create();
|
||||||
|
|
||||||
|
// Make sure recreate() is called on original activity
|
||||||
|
assertThat(mActivity.getRecreateCount()).isEqualTo(1);
|
||||||
|
|
||||||
|
// Simulate recreate() action
|
||||||
|
setupActivityWithGenerateChallenge(mActivity.getIntent());
|
||||||
|
mController.create();
|
||||||
|
|
||||||
|
// Verify confirmLock()
|
||||||
|
assertThat(mActivity.getConfirmingCredentials()).isTrue();
|
||||||
|
ShadowActivity shadowActivity = Shadows.shadowOf(mActivity);
|
||||||
|
ShadowActivity.IntentForResult startedActivity =
|
||||||
|
shadowActivity.getNextStartedActivityForResult();
|
||||||
|
assertWithMessage("Next activity 1").that(startedActivity).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_hasHeader() {
|
||||||
|
setupActivity();
|
||||||
|
TextView headerTextView = getGlifLayout(mActivity).findViewById(R.id.suc_layout_title);
|
||||||
|
|
||||||
|
assertThat(headerTextView).isNotNull();
|
||||||
|
assertThat(headerTextView.getText().toString()).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_hasDescription() {
|
||||||
|
setupActivity();
|
||||||
|
CharSequence desc = getGlifLayout(mActivity).getDescriptionText();
|
||||||
|
|
||||||
|
assertThat(desc.toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.security_settings_face_enroll_introduction_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_hasBottomScrollView() {
|
||||||
|
setupActivity();
|
||||||
|
BottomScrollView scrollView = getGlifLayout(mActivity).findViewById(R.id.sud_scroll_view);
|
||||||
|
|
||||||
|
assertThat(scrollView).isNotNull();
|
||||||
|
assertThat(scrollView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_showFooterPrimaryButton() {
|
||||||
|
setupActivity();
|
||||||
|
FooterBarMixin footer = getGlifLayout(mActivity).getMixin(FooterBarMixin.class);
|
||||||
|
FooterButton footerButton = footer.getPrimaryButton();
|
||||||
|
|
||||||
|
assertThat(footerButton).isNotNull();
|
||||||
|
assertThat(footerButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(footerButton.getText().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.security_settings_face_enroll_introduction_agree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_notShowFooterSecondaryButton() {
|
||||||
|
setupActivity();
|
||||||
|
FooterBarMixin footer = getGlifLayout(mActivity).getMixin(FooterBarMixin.class);
|
||||||
|
FooterButton footerButton = footer.getSecondaryButton();
|
||||||
|
|
||||||
|
assertThat(footerButton.getVisibility()).isEqualTo(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_defaultNeverLaunchPostureGuidance() {
|
||||||
|
setupActivity();
|
||||||
|
|
||||||
|
assertThat(mActivity.launchPostureGuidance()).isFalse();
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_onStartNeverRegisterPostureChangeCallback() {
|
||||||
|
setupActivity();
|
||||||
|
mActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mActivity.getPostureGuidanceIntent()).isNull();
|
||||||
|
assertThat(mActivity.getPostureCallback()).isNull();
|
||||||
|
assertThat(mActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_onStartRegisteredPostureChangeCallback() {
|
||||||
|
setupActivityForPosture();
|
||||||
|
mSpyActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mSpyActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mSpyActivity.getPostureCallback()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollIntroduction_onFoldedUpdated_unFolded() {
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.smallestScreenWidthDp = DENSITY_XXXHIGH;
|
||||||
|
setupActivityForPosture();
|
||||||
|
mSpyActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mSpyActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mSpyActivity.getPostureCallback()).isNotNull();
|
||||||
|
|
||||||
|
mSpyActivity.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
assertThat(mSpyActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_OPENED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaceEnrollEducation_onFoldedUpdated_folded() {
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.smallestScreenWidthDp = DENSITY_DEFAULT;
|
||||||
|
setupActivityForPosture();
|
||||||
|
mSpyActivity.onStart();
|
||||||
|
|
||||||
|
assertThat(mSpyActivity.getPostureGuidanceIntent()).isNotNull();
|
||||||
|
assertThat(mSpyActivity.getPostureCallback()).isNotNull();
|
||||||
|
|
||||||
|
mSpyActivity.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
assertThat(mSpyActivity.getDevicePostureState()).isEqualTo(DEVICE_POSTURE_CLOSED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user