[BiometricsV2] Rewrite Activity to Kotlin
Refactor FingerprintEnrollmentActivity as kotlin
Bug: 286197261
Test: atest FingerprintEnrollmentActivity
Test: atest biometrics-enrollment-test
Change-Id: I45d2db832b0111cb865b657aee56f84b0b295efa
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
deleted file mode 100644
index 76c9e95..0000000
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * Copyright (C) 2022 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.biometrics2.ui.view;
-
-import static androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
-import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
-import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
-
-import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY;
-import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY;
-import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.USER_ID_KEY;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
-import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorAction;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FingerprintEnrollFinishAction;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction;
-
-import android.annotation.StyleRes;
-import android.app.Application;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.activity.result.ActivityResult;
-import androidx.activity.result.ActivityResultCallback;
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.lifecycle.viewmodel.CreationExtras;
-import androidx.lifecycle.viewmodel.MutableCreationExtras;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.biometrics.BiometricEnrollBase;
-import com.android.settings.biometrics2.data.repository.FingerprintRepository;
-import com.android.settings.biometrics2.factory.BiometricsViewModelFactory;
-import com.android.settings.biometrics2.ui.model.CredentialModel;
-import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator;
-import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel;
-import com.android.settings.overlay.FeatureFactory;
-
-import com.google.android.setupdesign.util.ThemeHelper;
-
-/**
- * Fingerprint enrollment activity implementation
- */
-public class FingerprintEnrollmentActivity extends FragmentActivity {
-
- /**
- * Setupwizard activity
- */
- public static class SetupActivity extends FingerprintEnrollmentActivity {}
-
- /**
- * Internal activity for FingerprintSettings
- */
- public static class InternalActivity extends FingerprintEnrollmentActivity {}
-
- private static final boolean DEBUG = false;
- private static final String TAG = "FingerprintEnrollmentActivity";
-
- private static final String INTRO_TAG = "intro";
- private static final String FIND_SENSOR_TAG = "find-sensor";
- private static final String ENROLLING_TAG = "enrolling";
- private static final String FINISH_TAG = "finish";
- private static final String SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog";
- private static final String ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog";
-
- protected static final int LAUNCH_CONFIRM_LOCK_ACTIVITY = 1;
-
- // This flag is used for addBackStack(), we do not save it in ViewModel because it is just used
- // during FragmentManager calls
- private boolean mIsFirstFragmentAdded = false;
-
- private ViewModelProvider mViewModelProvider;
- private FingerprintEnrollmentViewModel mViewModel;
- private AutoCredentialViewModel mAutoCredentialViewModel;
- private final Observer<Integer> mIntroActionObserver = action -> {
- if (DEBUG) {
- Log.d(TAG, "mIntroActionObserver(" + action + ")");
- }
- if (action != null) {
- onIntroAction(action);
- }
- };
- private final Observer<Integer> mFindSensorActionObserver = action -> {
- if (DEBUG) {
- Log.d(TAG, "mFindSensorActionObserver(" + action + ")");
- }
- if (action != null) {
- onFindSensorAction(action);
- }
- };
- private final Observer<Integer> mEnrollingActionObserver = action -> {
- if (DEBUG) {
- Log.d(TAG, "mEnrollingActionObserver(" + action + ")");
- }
- if (action != null) {
- onEnrollingAction(action);
- }
- };
- private final Observer<ErrorDialogData> mEnrollingErrorDialogObserver = data -> {
- if (DEBUG) {
- Log.d(TAG, "mEnrollingErrorDialogObserver(" + data + ")");
- }
- if (data != null) {
- new FingerprintEnrollEnrollingErrorDialog().show(getSupportFragmentManager(),
- ENROLLING_ERROR_DIALOG_TAG);
- }
- };
- private final Observer<Integer> mEnrollingErrorDialogActionObserver = action -> {
- if (DEBUG) {
- Log.d(TAG, "mEnrollingErrorDialogActionObserver(" + action + ")");
- }
- if (action != null) {
- onEnrollingErrorDialogAction(action);
- }
- };
- private final Observer<Integer> mFinishActionObserver = action -> {
- if (DEBUG) {
- Log.d(TAG, "mFinishActionObserver(" + action + ")");
- }
- if (action != null) {
- onFinishAction(action);
- }
- };
- private final ActivityResultCallback<ActivityResult> mChooseLockResultCallback =
- result -> onChooseOrConfirmLockResult(true /* isChooseLock */, result);
- private final ActivityResultLauncher<Intent> mChooseLockLauncher =
- registerForActivityResult(new StartActivityForResult(), mChooseLockResultCallback);
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mViewModelProvider = new ViewModelProvider(this);
-
- mViewModel = mViewModelProvider.get(FingerprintEnrollmentViewModel.class);
- mViewModel.setSavedInstanceState(savedInstanceState);
-
- mAutoCredentialViewModel = mViewModelProvider.get(AutoCredentialViewModel.class);
- mAutoCredentialViewModel.setCredentialModel(savedInstanceState, getIntent());
-
- // Theme
- setTheme(mViewModel.getRequest().getTheme());
- ThemeHelper.trySetDynamicColor(this);
- getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
-
- // fragment
- setContentView(R.layout.biometric_enrollment_container);
-
- final Fragment fragment = getSupportFragmentManager().findFragmentById(
- R.id.fragment_container_view);
- if (DEBUG) {
- Log.d(TAG, "onCreate() has savedInstance:" + (savedInstanceState != null)
- + ", fragment:" + fragment);
- }
- if (fragment == null) {
- checkCredential();
- final EnrollmentRequest request = mViewModel.getRequest();
- if (request.isSkipFindSensor()) {
- startEnrollingFragment();
- } else if (request.isSkipIntro()) {
- startFindSensorFragment();
- } else {
- startIntroFragment();
- }
- } else {
- final String tag = fragment.getTag();
- if (INTRO_TAG.equals(tag)) {
- attachIntroViewModel();
- } else if (FIND_SENSOR_TAG.equals(tag)) {
- attachFindSensorViewModel();
- attachIntroViewModel();
- } else if (ENROLLING_TAG.equals(tag)) {
- attachEnrollingViewModel();
- attachFindSensorViewModel();
- attachIntroViewModel();
- } else if (FINISH_TAG.equals(tag)) {
- attachFinishViewModel();
- attachFindSensorViewModel();
- attachIntroViewModel();
- } else {
- Log.e(TAG, "fragment tag " + tag + " not found");
- finish();
- return;
- }
- }
-
- // observe LiveData
- mViewModel.getSetResultLiveData().observe(this, this::onSetActivityResult);
-
- mAutoCredentialViewModel.getGenerateChallengeFailedLiveData().observe(this,
- this::onGenerateChallengeFailed);
- }
-
- private void startFragment(@NonNull Class<? extends Fragment> fragmentClass,
- @NonNull String tag) {
- if (!mIsFirstFragmentAdded) {
- getSupportFragmentManager().beginTransaction()
- .setReorderingAllowed(true)
- .replace(R.id.fragment_container_view, fragmentClass, null, tag)
- .commit();
- mIsFirstFragmentAdded = true;
- } else {
- getSupportFragmentManager().beginTransaction()
- .setReorderingAllowed(true)
- .setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
- R.anim.shared_x_axis_activity_open_exit,
- R.anim.shared_x_axis_activity_close_enter_dynamic_color,
- R.anim.shared_x_axis_activity_close_exit)
- .replace(R.id.fragment_container_view, fragmentClass, null, tag)
- .addToBackStack(tag)
- .commit();
- }
- }
-
- private void startIntroFragment() {
- attachIntroViewModel();
- startFragment(FingerprintEnrollIntroFragment.class, INTRO_TAG);
- }
-
- private void attachIntroViewModel() {
- final EnrollmentRequest request = mViewModel.getRequest();
- if (request.isSkipIntro() || request.isSkipFindSensor()) {
- return;
- }
-
- final FingerprintEnrollIntroViewModel introViewModel =
- mViewModelProvider.get(FingerprintEnrollIntroViewModel.class);
-
- // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
- // recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor activity.
- introViewModel.clearActionLiveData();
- introViewModel.getActionLiveData().observe(this, mIntroActionObserver);
- }
-
- // We need to make sure token is valid before entering find sensor page
- private void startFindSensorFragment() {
- // Always setToken into progressViewModel even it is not necessary action for UDFPS
- mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
- .setToken(mAutoCredentialViewModel.getToken());
-
- attachFindSensorViewModel();
-
- final Class<? extends Fragment> fragmentClass;
- if (mViewModel.canAssumeUdfps()) {
- fragmentClass = FingerprintEnrollFindUdfpsFragment.class;
- } else if (mViewModel.canAssumeSfps()) {
- fragmentClass = FingerprintEnrollFindSfpsFragment.class;
- } else {
- fragmentClass = FingerprintEnrollFindRfpsFragment.class;
- }
- startFragment(fragmentClass, FIND_SENSOR_TAG);
- }
-
- private void attachFindSensorViewModel() {
- if (mViewModel.getRequest().isSkipFindSensor()) {
- return;
- }
-
- final FingerprintEnrollFindSensorViewModel findSensorViewModel =
- mViewModelProvider.get(FingerprintEnrollFindSensorViewModel.class);
-
- // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
- // recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling activity.
- findSensorViewModel.clearActionLiveData();
- findSensorViewModel.getActionLiveData().observe(this, mFindSensorActionObserver);
- }
-
- private void startEnrollingFragment() {
- // Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
- mViewModelProvider.get(FingerprintEnrollProgressViewModel.class)
- .setToken(mAutoCredentialViewModel.getToken());
-
- attachEnrollingViewModel();
-
- final Class<? extends Fragment> fragmentClass;
- if (mViewModel.canAssumeUdfps()) {
- fragmentClass = FingerprintEnrollEnrollingUdfpsFragment.class;
- } else if (mViewModel.canAssumeSfps()) {
- fragmentClass = FingerprintEnrollEnrollingSfpsFragment.class;
- } else {
- fragmentClass = FingerprintEnrollEnrollingRfpsFragment.class;
- }
- startFragment(fragmentClass, ENROLLING_TAG);
- }
-
- private void attachEnrollingViewModel() {
- final FingerprintEnrollEnrollingViewModel enrollingViewModel =
- mViewModelProvider.get(FingerprintEnrollEnrollingViewModel.class);
- enrollingViewModel.clearActionLiveData();
- enrollingViewModel.getActionLiveData().observe(this, mEnrollingActionObserver);
- enrollingViewModel.getErrorDialogLiveData().observe(this, mEnrollingErrorDialogObserver);
- enrollingViewModel.getErrorDialogActionLiveData().observe(this,
- mEnrollingErrorDialogActionObserver);
- }
-
- private void startFinishFragment() {
- mViewModel.setIsNewFingerprintAdded();
- attachFinishViewModel();
-
- if (mViewModel.getRequest().isSkipFindSensor()) {
- // Set page to Finish
- getSupportFragmentManager().beginTransaction()
- .setReorderingAllowed(true)
- .setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
- R.anim.shared_x_axis_activity_open_exit,
- R.anim.shared_x_axis_activity_close_enter_dynamic_color,
- R.anim.shared_x_axis_activity_close_exit)
- .replace(R.id.fragment_container_view, FingerprintEnrollFinishFragment.class,
- null, FINISH_TAG)
- .commit();
- } else {
- // Remove Enrolling page
- getSupportFragmentManager().popBackStack();
-
- // Remove old Finish page if any
- if (getSupportFragmentManager().findFragmentByTag(FINISH_TAG) != null) {
- getSupportFragmentManager().popBackStack(FINISH_TAG, POP_BACK_STACK_INCLUSIVE);
- }
-
- // Remove FindSensor page if maxEnrolled
- if (mViewModel.isMaxEnrolledReached(mAutoCredentialViewModel.getUserId())
- && getSupportFragmentManager().findFragmentByTag(FIND_SENSOR_TAG) != null) {
- getSupportFragmentManager().popBackStack(FIND_SENSOR_TAG, POP_BACK_STACK_INCLUSIVE);
- }
-
- // Add Finish page
- getSupportFragmentManager().beginTransaction()
- .setReorderingAllowed(true)
- .setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
- R.anim.shared_x_axis_activity_open_exit,
- R.anim.shared_x_axis_activity_close_enter_dynamic_color,
- R.anim.shared_x_axis_activity_close_exit)
- .replace(R.id.fragment_container_view, FingerprintEnrollFinishFragment.class,
- null, FINISH_TAG)
- .addToBackStack(FINISH_TAG)
- .commit();
- }
- }
-
- private void attachFinishViewModel() {
- final FingerprintEnrollFinishViewModel viewModel =
- mViewModelProvider.get(FingerprintEnrollFinishViewModel.class);
- viewModel.clearActionLiveData();
- viewModel.getActionLiveData().observe(this, mFinishActionObserver);
- }
-
- private void onGenerateChallengeFailed(@NonNull Boolean ignoredBoolean) {
- onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
- }
-
- private void onSetActivityResult(@NonNull ActivityResult result) {
- final Bundle challengeExtras = mAutoCredentialViewModel.createGeneratingChallengeExtras();
- final ActivityResult overrideResult = mViewModel.getOverrideActivityResult(
- result, challengeExtras);
- if (DEBUG) {
- Log.d(TAG, "onSetActivityResult(" + result + "), override:" + overrideResult
- + ") challengeExtras:" + challengeExtras);
- }
- setResult(overrideResult.getResultCode(), overrideResult.getData());
- finish();
- }
-
- private void checkCredential() {
- switch (mAutoCredentialViewModel.checkCredential()) {
- case CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK: {
- final Intent intent = mAutoCredentialViewModel.createChooseLockIntent(this,
- mViewModel.getRequest().isSuw(), mViewModel.getRequest().getSuwExtras());
- if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
- Log.w(TAG, "chooseLock, fail to set isWaiting flag to true");
- }
- mChooseLockLauncher.launch(intent);
- return;
- }
- case CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK: {
- final boolean launched = mAutoCredentialViewModel.createConfirmLockLauncher(
- this,
- LAUNCH_CONFIRM_LOCK_ACTIVITY,
- getString(R.string.security_settings_fingerprint_preference_title)
- ).launch();
- if (!launched) {
- // This shouldn't happen, as we should only end up at this step if a lock thingy
- // is already set.
- Log.e(TAG, "confirmLock, launched is true");
- finish();
- } else if (!mViewModel.isWaitingActivityResult().compareAndSet(false, true)) {
- Log.w(TAG, "confirmLock, fail to set isWaiting flag to true");
- }
- return;
- }
- case CREDENTIAL_VALID:
- case CREDENTIAL_IS_GENERATING_CHALLENGE: {
- // Do nothing
- }
- }
- }
-
- private void onChooseOrConfirmLockResult(boolean isChooseLock,
- @NonNull ActivityResult activityResult) {
- if (!mViewModel.isWaitingActivityResult().compareAndSet(true, false)) {
- Log.w(TAG, "isChooseLock:" + isChooseLock + ", fail to unset waiting flag");
- }
- if (mAutoCredentialViewModel.checkNewCredentialFromActivityResult(
- isChooseLock, activityResult)) {
- overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
- } else {
- onSetActivityResult(activityResult);
- }
- }
-
- private void onIntroAction(@FingerprintEnrollIntroAction int action) {
- switch (action) {
- case FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH: {
- onSetActivityResult(
- new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null));
- return;
- }
- case FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL: {
- onSetActivityResult(
- new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
- return;
- }
- case FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL: {
- startFindSensorFragment();
- }
- }
- }
-
- private void onFindSensorAction(@FingerprintEnrollFindSensorAction int action) {
- switch (action) {
- case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP: {
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
- return;
- }
- case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG: {
- new SkipSetupFindFpsDialog().show(getSupportFragmentManager(),
- SKIP_SETUP_FIND_FPS_DIALOG_TAG);
- return;
- }
- case FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START: {
- startEnrollingFragment();
- }
- }
- }
-
- private void onEnrollingAction(@FingerprintEnrollEnrollingAction int action) {
- switch (action) {
- case FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE: {
- startFinishFragment();
- break;
- }
- case FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP: {
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_SKIP, null));
- break;
- }
- case FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG: {
- new FingerprintEnrollEnrollingIconTouchDialog().show(getSupportFragmentManager(),
- SKIP_SETUP_FIND_FPS_DIALOG_TAG);
- break;
- }
- case FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED: {
- if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
- getSupportFragmentManager().popBackStack();
- } else {
- onSetActivityResult(new ActivityResult(RESULT_CANCELED, null));
- }
- break;
- }
- }
- }
-
- private void onEnrollingErrorDialogAction(@FingerprintErrorDialogAction int action) {
- switch (action) {
- case FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH:
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null));
- break;
- case FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT:
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null));
- break;
- }
- }
-
- private void onFinishAction(@FingerprintEnrollFinishAction int action) {
- switch (action) {
- case FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK: {
- startEnrollingFragment();
- break;
- }
- case FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK: {
- final Intent data;
- if (mViewModel.getRequest().isSuw()) {
- data = new Intent();
- data.putExtras(mViewModel.getSuwFingerprintCountExtra(
- mAutoCredentialViewModel.getUserId()));
- } else {
- data = null;
- }
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_FINISHED, data));
- break;
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mViewModel.checkFinishActivityDuringOnPause(isFinishing(), isChangingConfigurations());
- }
-
- @Override
- protected void onDestroy() {
- mViewModel.updateFingerprintSuggestionEnableState(mAutoCredentialViewModel.getUserId());
- super.onDestroy();
- }
-
- @Override
- protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid, boolean first) {
- theme.applyStyle(R.style.SetupWizardPartnerResource, true);
- super.onApplyThemeResource(theme, resid, first);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode == LAUNCH_CONFIRM_LOCK_ACTIVITY) {
- onChooseOrConfirmLockResult(false, new ActivityResult(resultCode, data));
- return;
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- @NonNull
- @Override
- public CreationExtras getDefaultViewModelCreationExtras() {
- final Application application =
- super.getDefaultViewModelCreationExtras().get(APPLICATION_KEY);
- final MutableCreationExtras ret = new MutableCreationExtras();
- ret.set(APPLICATION_KEY, application);
-
- final FingerprintRepository repository = FeatureFactory.getFactory(application)
- .getBiometricsRepositoryProvider().getFingerprintRepository(application);
- ret.set(CHALLENGE_GENERATOR_KEY, new FingerprintChallengeGenerator(repository));
-
- ret.set(ENROLLMENT_REQUEST_KEY, new EnrollmentRequest(getIntent(), getApplicationContext(),
- this instanceof SetupActivity));
-
- Bundle extras = getIntent().getExtras();
- final CredentialModel credentialModel = new CredentialModel(extras,
- SystemClock.elapsedRealtimeClock());
- ret.set(USER_ID_KEY, credentialModel.getUserId());
-
- return ret;
- }
-
- @NonNull
- @Override
- public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
- return new BiometricsViewModelFactory();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- getWindow().setStatusBarColor(getBackgroundColor());
- }
-
- @ColorInt
- private int getBackgroundColor() {
- final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
- return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
- }
-
- @Override
- public void onConfigurationChanged(@NonNull Configuration newConfig) {
- mViewModelProvider.get(DeviceFoldedViewModel.class).onConfigurationChanged(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- mViewModel.onSaveInstanceState(outState);
- mAutoCredentialViewModel.onSaveInstanceState(outState);
- }
-}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
new file mode 100644
index 0000000..e3a6078
--- /dev/null
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
@@ -0,0 +1,644 @@
+/*
+ * 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.biometrics2.ui.view
+
+import android.annotation.StyleRes
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.content.res.Resources.Theme
+import android.graphics.Color
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Log
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.ActivityResultCallback
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.annotation.ColorInt
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewmodel.CreationExtras
+import androidx.lifecycle.viewmodel.MutableCreationExtras
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.biometrics.BiometricEnrollBase
+import com.android.settings.biometrics2.data.repository.FingerprintRepository
+import com.android.settings.biometrics2.factory.BiometricsViewModelFactory
+import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY
+import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY
+import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.USER_ID_KEY
+import com.android.settings.biometrics2.ui.model.CredentialModel
+import com.android.settings.biometrics2.ui.model.EnrollmentRequest
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID
+import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator
+import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorAction
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FingerprintEnrollFinishAction
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
+import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel
+import com.android.settings.overlay.FeatureFactory
+import com.google.android.setupdesign.util.ThemeHelper
+
+/**
+ * Fingerprint enrollment activity implementation
+ */
+open class FingerprintEnrollmentActivity : FragmentActivity() {
+ /** SetupWizard activity*/
+ class SetupActivity : FingerprintEnrollmentActivity()
+
+ /** Internal activity for FingerprintSettings */
+ class InternalActivity : FingerprintEnrollmentActivity()
+
+ /**
+ * This flag is used for addBackStack(), we do not save it in ViewModel because it is just used
+ * during FragmentManager calls
+ */
+ private var isFirstFragmentAdded = false
+
+ private val viewModelProvider: ViewModelProvider by lazy {
+ ViewModelProvider(this)
+ }
+
+ private val viewModel: FingerprintEnrollmentViewModel by lazy {
+ viewModelProvider[FingerprintEnrollmentViewModel::class.java]
+ }
+
+ private val autoCredentialViewModel: AutoCredentialViewModel by lazy {
+ viewModelProvider[AutoCredentialViewModel::class.java]
+ }
+
+ private val introActionObserver: Observer<Int> = Observer<Int> { action ->
+ if (DEBUG) {
+ Log.d(TAG, "introActionObserver($action)")
+ }
+ action?.let { onIntroAction(it) }
+ }
+
+ private val findSensorActionObserver: Observer<Int> = Observer<Int> { action ->
+ if (DEBUG) {
+ Log.d(TAG, "findSensorActionObserver($action)")
+ }
+ action?.let { onFindSensorAction(it) }
+ }
+
+ private val enrollingActionObserver: Observer<Int> = Observer<Int> { action ->
+ if (DEBUG) {
+ Log.d(TAG, "enrollingActionObserver($action)")
+ }
+ action?.let { onEnrollingAction(it) }
+ }
+
+ private val enrollingErrorDialogObserver: Observer<ErrorDialogData> =
+ Observer<ErrorDialogData> { data ->
+ if (DEBUG) {
+ Log.d(TAG, "enrollingErrorDialogObserver($data)")
+ }
+ data?.let {
+ FingerprintEnrollEnrollingErrorDialog().show(
+ supportFragmentManager,
+ ENROLLING_ERROR_DIALOG_TAG
+ )
+ }
+ }
+
+ private val enrollingErrorDialogActionObserver: Observer<Int> = Observer<Int> { action ->
+ if (DEBUG) {
+ Log.d(TAG, "enrollingErrorDialogActionObserver($action)")
+ }
+ action?.let { onEnrollingErrorDialogAction(it) }
+ }
+
+ private val finishActionObserver: Observer<Int> = Observer<Int> { action ->
+ if (DEBUG) {
+ Log.d(TAG, "finishActionObserver($action)")
+ }
+ action?.let { onFinishAction(it) }
+ }
+
+ private val chooseLockResultCallback: ActivityResultCallback<ActivityResult> =
+ ActivityResultCallback { result ->
+ onChooseOrConfirmLockResult(true /* isChooseLock */, result)
+ }
+
+ private val chooseLockLauncher: ActivityResultLauncher<Intent> =
+ registerForActivityResult(StartActivityForResult(), chooseLockResultCallback)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ viewModel.setSavedInstanceState(savedInstanceState)
+ autoCredentialViewModel.setCredentialModel(savedInstanceState, intent)
+
+ // Theme
+ setTheme(viewModel.request.theme)
+ ThemeHelper.trySetDynamicColor(this)
+ window.statusBarColor = Color.TRANSPARENT
+
+ // fragment
+ setContentView(R.layout.biometric_enrollment_container)
+ val fragment: Fragment? = supportFragmentManager.findFragmentById(
+ R.id.fragment_container_view
+ )
+ if (DEBUG) {
+ Log.d(
+ TAG, "onCreate() has savedInstance:" + (savedInstanceState != null)
+ + ", fragment:" + fragment
+ )
+ }
+ if (fragment == null) {
+ checkCredential()
+ val request: EnrollmentRequest = viewModel.getRequest()
+ if (request.isSkipFindSensor) {
+ startEnrollingFragment()
+ } else if (request.isSkipIntro) {
+ startFindSensorFragment()
+ } else {
+ startIntroFragment()
+ }
+ } else {
+ val tag: String? = fragment.tag
+ if (INTRO_TAG == tag) {
+ attachIntroViewModel()
+ } else if (FIND_SENSOR_TAG == tag) {
+ attachFindSensorViewModel()
+ attachIntroViewModel()
+ } else if (ENROLLING_TAG == tag) {
+ attachEnrollingViewModel()
+ attachFindSensorViewModel()
+ attachIntroViewModel()
+ } else if (FINISH_TAG == tag) {
+ attachFinishViewModel()
+ attachFindSensorViewModel()
+ attachIntroViewModel()
+ } else {
+ Log.e(TAG, "fragment tag $tag not found")
+ finish()
+ return
+ }
+ }
+
+ // observe LiveData
+ viewModel.setResultLiveData.observe(this) {
+ result: ActivityResult -> onSetActivityResult(result)
+ }
+ autoCredentialViewModel.generateChallengeFailedLiveData.observe(this) {
+ _: Boolean -> onGenerateChallengeFailed()
+ }
+ }
+
+ private fun startFragment(fragmentClass: Class<out Fragment>, tag: String) {
+ if (!isFirstFragmentAdded) {
+ supportFragmentManager.beginTransaction()
+ .setReorderingAllowed(true)
+ .replace(R.id.fragment_container_view, fragmentClass, null, tag)
+ .commit()
+ isFirstFragmentAdded = true
+ } else {
+ supportFragmentManager.beginTransaction()
+ .setReorderingAllowed(true)
+ .setCustomAnimations(
+ R.anim.shared_x_axis_activity_open_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_open_exit,
+ R.anim.shared_x_axis_activity_close_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_close_exit
+ )
+ .replace(R.id.fragment_container_view, fragmentClass, null, tag)
+ .addToBackStack(tag)
+ .commit()
+ }
+ }
+
+ private fun startIntroFragment() {
+ attachIntroViewModel()
+ startFragment(FingerprintEnrollIntroFragment::class.java, INTRO_TAG)
+ }
+
+ private fun attachIntroViewModel() {
+ val request: EnrollmentRequest = viewModel.request
+ if (request.isSkipIntro || request.isSkipFindSensor) {
+ return
+ }
+ viewModelProvider[FingerprintEnrollIntroViewModel::class.java].let {
+ // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
+ // recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor
+ // activity.
+ it.clearActionLiveData()
+ it.actionLiveData.observe(this, introActionObserver)
+ }
+ }
+
+ // We need to make sure token is valid before entering find sensor page
+ private fun startFindSensorFragment() {
+ // Always setToken into progressViewModel even it is not necessary action for UDFPS
+ viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
+ .setToken(autoCredentialViewModel.token)
+ attachFindSensorViewModel()
+ val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
+ FingerprintEnrollFindUdfpsFragment::class.java
+ } else if (viewModel.canAssumeSfps()) {
+ FingerprintEnrollFindSfpsFragment::class.java
+ } else {
+ FingerprintEnrollFindRfpsFragment::class.java
+ }
+ startFragment(fragmentClass, FIND_SENSOR_TAG)
+ }
+
+ private fun attachFindSensorViewModel() {
+ if (viewModel.request.isSkipFindSensor) {
+ return
+ }
+ viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java].let {
+ // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
+ // recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling
+ // activity.
+ it.clearActionLiveData()
+ it.actionLiveData.observe(this, findSensorActionObserver)
+ }
+ }
+
+ private fun startEnrollingFragment() {
+ // Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
+ viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
+ .setToken(autoCredentialViewModel.token)
+ attachEnrollingViewModel()
+ val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
+ FingerprintEnrollEnrollingUdfpsFragment::class.java
+ } else if (viewModel.canAssumeSfps()) {
+ FingerprintEnrollEnrollingSfpsFragment::class.java
+ } else {
+ FingerprintEnrollEnrollingRfpsFragment::class.java
+ }
+ startFragment(fragmentClass, ENROLLING_TAG)
+ }
+
+ private fun attachEnrollingViewModel() {
+ viewModelProvider[FingerprintEnrollEnrollingViewModel::class.java].let {
+ it.clearActionLiveData()
+ it.actionLiveData.observe(this, enrollingActionObserver)
+ it.errorDialogLiveData.observe(this, enrollingErrorDialogObserver)
+ it.errorDialogActionLiveData.observe(
+ this,
+ enrollingErrorDialogActionObserver
+ )
+ }
+ }
+
+ private fun startFinishFragment() {
+ viewModel.setIsNewFingerprintAdded()
+ attachFinishViewModel()
+ if (viewModel.request.isSkipFindSensor) {
+ // Set page to Finish
+ supportFragmentManager.beginTransaction()
+ .setReorderingAllowed(true)
+ .setCustomAnimations(
+ R.anim.shared_x_axis_activity_open_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_open_exit,
+ R.anim.shared_x_axis_activity_close_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_close_exit
+ )
+ .replace(
+ R.id.fragment_container_view,
+ FingerprintEnrollFinishFragment::class.java,
+ null,
+ FINISH_TAG
+ )
+ .commit()
+ } else {
+ // Remove Enrolling page
+ supportFragmentManager.popBackStack()
+
+ // Remove old Finish page if any
+ if (supportFragmentManager.findFragmentByTag(FINISH_TAG) != null) {
+ supportFragmentManager.popBackStack(FINISH_TAG, POP_BACK_STACK_INCLUSIVE)
+ }
+
+ // Remove FindSensor page if maxEnrolled
+ if (viewModel.isMaxEnrolledReached(autoCredentialViewModel.userId)
+ && supportFragmentManager.findFragmentByTag(FIND_SENSOR_TAG) != null
+ ) {
+ supportFragmentManager.popBackStack(FIND_SENSOR_TAG, POP_BACK_STACK_INCLUSIVE)
+ }
+
+ // Add Finish page
+ supportFragmentManager.beginTransaction()
+ .setReorderingAllowed(true)
+ .setCustomAnimations(
+ R.anim.shared_x_axis_activity_open_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_open_exit,
+ R.anim.shared_x_axis_activity_close_enter_dynamic_color,
+ R.anim.shared_x_axis_activity_close_exit
+ )
+ .replace(
+ R.id.fragment_container_view,
+ FingerprintEnrollFinishFragment::class.java,
+ null,
+ FINISH_TAG
+ )
+ .addToBackStack(FINISH_TAG)
+ .commit()
+ }
+ }
+
+ private fun attachFinishViewModel() {
+ viewModelProvider[FingerprintEnrollFinishViewModel::class.java].let {
+ it.clearActionLiveData()
+ it.actionLiveData.observe(this, finishActionObserver)
+ }
+ }
+
+ private fun onGenerateChallengeFailed() {
+ onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
+ }
+
+ private fun onSetActivityResult(result: ActivityResult) {
+ val challengeExtras: Bundle? = autoCredentialViewModel.createGeneratingChallengeExtras()
+ val overrideResult: ActivityResult = viewModel.getOverrideActivityResult(
+ result, challengeExtras
+ )
+ if (DEBUG) {
+ Log.d(
+ TAG, "onSetActivityResult(" + result + "), override:" + overrideResult
+ + ") challengeExtras:" + challengeExtras
+ )
+ }
+ setResult(overrideResult.resultCode, overrideResult.data)
+ finish()
+ }
+
+ private fun checkCredential() {
+ when (autoCredentialViewModel.checkCredential()) {
+ CREDENTIAL_FAIL_NEED_TO_CHOOSE_LOCK -> {
+ val intent: Intent = autoCredentialViewModel.createChooseLockIntent(
+ this,
+ viewModel.request.isSuw,
+ viewModel.request.suwExtras
+ )
+ if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) {
+ Log.w(TAG, "chooseLock, fail to set isWaiting flag to true")
+ }
+ chooseLockLauncher.launch(intent)
+ return
+ }
+
+ CREDENTIAL_FAIL_NEED_TO_CONFIRM_LOCK -> {
+ val launched: Boolean = autoCredentialViewModel.createConfirmLockLauncher(
+ this,
+ LAUNCH_CONFIRM_LOCK_ACTIVITY,
+ getString(R.string.security_settings_fingerprint_preference_title)
+ ).launch()
+ if (!launched) {
+ // This shouldn't happen, as we should only end up at this step if a lock thingy
+ // is already set.
+ Log.e(TAG, "confirmLock, launched is true")
+ finish()
+ } else if (!viewModel.isWaitingActivityResult().compareAndSet(false, true)) {
+ Log.w(TAG, "confirmLock, fail to set isWaiting flag to true")
+ }
+ return
+ }
+
+ CREDENTIAL_VALID,
+ CREDENTIAL_IS_GENERATING_CHALLENGE -> {}
+ }
+ }
+
+ private fun onChooseOrConfirmLockResult(isChooseLock: Boolean, activityResult: ActivityResult) {
+ if (!viewModel.isWaitingActivityResult().compareAndSet(true, false)) {
+ Log.w(TAG, "isChooseLock:$isChooseLock, fail to unset waiting flag")
+ }
+ if (autoCredentialViewModel.checkNewCredentialFromActivityResult(
+ isChooseLock, activityResult
+ )
+ ) {
+ overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out)
+ } else {
+ onSetActivityResult(activityResult)
+ }
+ }
+
+ private fun onIntroAction(@FingerprintEnrollIntroAction action: Int) {
+ when (action) {
+ FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH -> {
+ onSetActivityResult(
+ ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
+ )
+ return
+ }
+
+ FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL -> {
+ onSetActivityResult(
+ ActivityResult(BiometricEnrollBase.RESULT_SKIP, null)
+ )
+ return
+ }
+
+ FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL -> {
+ startFindSensorFragment()
+ }
+ }
+ }
+
+ private fun onFindSensorAction(@FingerprintEnrollFindSensorAction action: Int) {
+ when (action) {
+ FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP -> {
+ onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_SKIP, null))
+ return
+ }
+
+ FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG -> {
+ SkipSetupFindFpsDialog().show(
+ supportFragmentManager,
+ SKIP_SETUP_FIND_FPS_DIALOG_TAG
+ )
+ return
+ }
+
+ FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START -> {
+ startEnrollingFragment()
+ }
+ }
+ }
+
+ private fun onEnrollingAction(@FingerprintEnrollEnrollingAction action: Int) {
+ when (action) {
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE -> {
+ startFinishFragment()
+ }
+
+ FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP -> {
+ onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_SKIP, null))
+ }
+
+ FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG -> {
+ FingerprintEnrollEnrollingIconTouchDialog().show(
+ supportFragmentManager,
+ SKIP_SETUP_FIND_FPS_DIALOG_TAG
+ )
+ }
+
+ FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED -> {
+ if (supportFragmentManager.backStackEntryCount > 0) {
+ supportFragmentManager.popBackStack()
+ } else {
+ onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
+ }
+ }
+ }
+ }
+
+ private fun onEnrollingErrorDialogAction(@FingerprintErrorDialogAction action: Int) {
+ when (action) {
+ FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH -> onSetActivityResult(
+ ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
+ )
+
+ FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT -> onSetActivityResult(
+ ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)
+ )
+ }
+ }
+
+ private fun onFinishAction(@FingerprintEnrollFinishAction action: Int) {
+ when (action) {
+ FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK -> {
+ startEnrollingFragment()
+ }
+
+ FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK -> {
+ val data: Intent? = if (viewModel.request.isSuw) {
+ Intent().also {
+ it.putExtras(
+ viewModel.getSuwFingerprintCountExtra(
+ autoCredentialViewModel.userId
+ )
+ )
+ }
+ } else {
+ null
+ }
+ onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_FINISHED, data))
+ }
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ viewModel.checkFinishActivityDuringOnPause(isFinishing, isChangingConfigurations)
+ }
+
+ override fun onDestroy() {
+ viewModel.updateFingerprintSuggestionEnableState(autoCredentialViewModel.userId)
+ super.onDestroy()
+ }
+
+ override fun onApplyThemeResource(theme: Theme, @StyleRes resid: Int, first: Boolean) {
+ theme.applyStyle(R.style.SetupWizardPartnerResource, true)
+ super.onApplyThemeResource(theme, resid, first)
+ }
+
+ @Deprecated("Deprecated in Java")
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == LAUNCH_CONFIRM_LOCK_ACTIVITY) {
+ onChooseOrConfirmLockResult(false, ActivityResult(resultCode, data))
+ return
+ }
+ super.onActivityResult(requestCode, resultCode, data)
+ }
+
+ override val defaultViewModelCreationExtras: CreationExtras
+ get() {
+ val fingerprintRepository = FeatureFactory
+ .getFactory(application)
+ .biometricsRepositoryProvider
+ .getFingerprintRepository(application)!!
+ val credentialModel = CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
+
+ return MutableCreationExtras(super.defaultViewModelCreationExtras).also {
+ it[CHALLENGE_GENERATOR_KEY] = FingerprintChallengeGenerator(fingerprintRepository)
+ it[ENROLLMENT_REQUEST_KEY] =
+ EnrollmentRequest(intent, applicationContext, this is SetupActivity)
+ it[USER_ID_KEY] = credentialModel.userId
+ }
+ }
+
+ override val defaultViewModelProviderFactory: ViewModelProvider.Factory
+ get() = BiometricsViewModelFactory()
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ window.statusBarColor = backgroundColor
+ }
+
+ @get:ColorInt
+ private val backgroundColor: Int
+ get() {
+ val stateList: ColorStateList? =
+ Utils.getColorAttr(this, android.R.attr.windowBackground)
+ return stateList?.defaultColor ?: Color.TRANSPARENT
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ viewModelProvider[DeviceFoldedViewModel::class.java].onConfigurationChanged(newConfig)
+ super.onConfigurationChanged(newConfig)
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ viewModel.onSaveInstanceState(outState)
+ autoCredentialViewModel.onSaveInstanceState(outState)
+ }
+
+ companion object {
+ private const val DEBUG = false
+ private const val TAG = "FingerprintEnrollmentActivity"
+ private const val INTRO_TAG = "intro"
+ private const val FIND_SENSOR_TAG = "find-sensor"
+ private const val ENROLLING_TAG = "enrolling"
+ private const val FINISH_TAG = "finish"
+ private const val SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog"
+ private const val ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog"
+ protected const val LAUNCH_CONFIRM_LOCK_ACTIVITY = 1
+ }
+}
diff --git a/tests/uitests/Android.bp b/tests/uitests/Android.bp
index d4f09a3..407b5ec 100644
--- a/tests/uitests/Android.bp
+++ b/tests/uitests/Android.bp
@@ -26,7 +26,10 @@
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
libs: [
"android.test.runner",
@@ -34,6 +37,7 @@
],
static_libs: [
+ "androidx.test.ext.junit",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"app-helpers-core",
diff --git a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java
deleted file mode 100644
index 0a84c15..0000000
--- a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (C) 2022 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.biometrics2.ui.view;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
-import android.os.UserHandle;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.settings.biometrics2.utils.LockScreenUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class FingerprintEnrollmentActivityTest {
-
- private static final String TAG = "FingerprintEnrollmentActivityTest";
-
- private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
- private static final String ACTIVITY_CLASS_NAME =
- "com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity";
- private static final String SUW_ACTIVITY_CLASS_NAME = ACTIVITY_CLASS_NAME + "$SetupActivity";
- private static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
- private static final String EXTRA_SKIP_INTRO = "skip_intro";
- private static final String EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor";
- private static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type";
- private static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
- private static final String TEST_PIN = "1234";
-
- private static final String DO_IT_LATER = "Do it later";
-
- private static final String UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor";
- private static final String SFPS_ENROLLING_TITLE =
- "Lift, then touch. Move your finger slightly each time.";
- private static final String RFPS_ENROLLING_TITLE = "Lift, then touch again";
-
- private UiDevice mDevice;
- private byte[] mToken = new byte[]{};
- private Context mContext;
- private boolean mFingerprintPropCallbackLaunched;
- private boolean mCanAssumeUdfps;
- private boolean mCanAssumeSfps;
- private String mEnrollingTitle;
-
- private static final int IDLE_TIMEOUT = 10000;
-
- @Before
- public void setUp() throws InterruptedException {
- mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- mContext = InstrumentationRegistry.getContext();
-
- // Stop every test if it is not a fingerprint device
- assumeTrue(mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_FINGERPRINT));
-
- final FingerprintManager fingerprintManager = mContext.getSystemService(
- FingerprintManager.class);
- mFingerprintPropCallbackLaunched = false;
- fingerprintManager.addAuthenticatorsRegisteredCallback(
- new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
- @Override
- public void onAllAuthenticatorsRegistered(
- List<FingerprintSensorPropertiesInternal> list) {
- mFingerprintPropCallbackLaunched = true;
-
- assertThat(list).isNotNull();
- assertThat(list).isNotEmpty();
- final FingerprintSensorPropertiesInternal prop = list.get(0);
- mCanAssumeUdfps = prop.isAnyUdfpsType();
- mCanAssumeSfps = prop.isAnySidefpsType();
- if (mCanAssumeUdfps) {
- mEnrollingTitle = UDFPS_ENROLLING_TITLE;
- } else if (mCanAssumeSfps) {
- mEnrollingTitle = SFPS_ENROLLING_TITLE;
- } else {
- mEnrollingTitle = RFPS_ENROLLING_TITLE;
- }
- }
- });
-
- for (long i = 0; i < IDLE_TIMEOUT && !mFingerprintPropCallbackLaunched; i += 100L) {
- Thread.sleep(100L);
- }
- assertThat(mFingerprintPropCallbackLaunched).isTrue();
-
- mDevice.pressHome();
-
- // Stop settings before performing test
- try {
- mDevice.executeShellCommand("am force-stop " + SETTINGS_PACKAGE_NAME);
- } catch (IOException e) {
- Log.e(TAG, "Fail to stop settings app", e);
- }
- }
-
- @After
- public void tearDown() throws Exception {
- LockScreenUtil.resetLockscreen(TEST_PIN);
- mDevice.pressHome();
- }
-
- @Test
- public void testIntroChooseLock() {
- final Intent intent = newActivityIntent(false);
- mContext.startActivity(intent);
- assertThat(mDevice.wait(Until.hasObject(By.text("Choose your backup screen lock method")),
- IDLE_TIMEOUT)).isTrue();
- }
-
- private void verifyIntroPage() {
- mDevice.waitForIdle();
- for (long i = 0; i < IDLE_TIMEOUT; i += 100L) {
- if (mDevice.wait(Until.hasObject(By.text("More")), 50L)) {
- break;
- } else if (mDevice.wait(Until.hasObject(By.text("I agree")), 50L)) {
- break;
- }
- }
-
- // Click more btn at most twice and the introduction should stay in the last page
- UiObject2 moreBtn;
- for (int i = 0; i < 2 && (moreBtn = mDevice.findObject(By.text("More"))) != null; ++i) {
- moreBtn.click();
- mDevice.waitForIdle();
- mDevice.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT);
- }
-
- assertThat(mDevice.wait(Until.hasObject(By.text("No thanks")), IDLE_TIMEOUT)).isTrue();
- assertThat(mDevice.wait(Until.hasObject(By.text("I agree")), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testIntroWithGkPwHandle_withUdfps_clickStart() {
- assumeTrue(mCanAssumeUdfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindUdfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
- assertThat(lottie.isClickable()).isTrue();
- final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
- assertThat(startBtn.isClickable()).isTrue();
- startBtn.click();
-
- // Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testIntroWithGkPwHandle_withUdfps_clickLottie() {
- assumeTrue(mCanAssumeUdfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindUdfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
- assertThat(lottie.isClickable()).isTrue();
- final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
- assertThat(startBtn.isClickable()).isTrue();
- lottie.click();
-
- // Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testIntroWithGkPwHandle_withSfps() {
- assumeTrue(mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindSfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
-
- // We don't have view which can be clicked to run to next page, stop at here.
- }
-
- @Test
- public void testIntroWithGkPwHandle_withRfps() {
- assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindRfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- if (lottie == null) {
- // FindSfps page shall have an animation view if no lottie view
- assertThat(mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "fingerprint_sensor_location_animation"))).isNotNull();
- }
- }
-
- @Test
- public void testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 noThanksBtn = mDevice.findObject(By.text("No thanks"));
- assertThat(noThanksBtn).isNotNull();
- noThanksBtn.click();
-
- // Back to home
- mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
- assertThat(mDevice.findObject(By.text("No thanks"))).isNull();
- }
-
- @Test
- public void testIntroWithGkPwHandle_clickSkipInFindSensor() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(false);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindSensor page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
- assertThat(doItLaterBtn).isNotNull();
- assertThat(doItLaterBtn.isClickable()).isTrue();
- doItLaterBtn.click();
-
- // Back to home
- mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
- assertThat(mDevice.findObject(By.text(DO_IT_LATER))).isNull();
- }
-
- @Test
- public void testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(true);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindSensor page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
- assertThat(doItLaterBtn).isNotNull();
- assertThat(doItLaterBtn.isClickable()).isTrue();
- doItLaterBtn.click();
-
- // SkipSetupFindFpsDialog
- assertThat(mDevice.wait(Until.hasObject(By.text("Skip fingerprint?")),
- IDLE_TIMEOUT)).isTrue();
- final UiObject2 skipAnywayBtn = mDevice.findObject(By.text("Skip anyway"));
- assertThat(skipAnywayBtn).isNotNull();
- assertThat(skipAnywayBtn.isClickable()).isTrue();
- skipAnywayBtn.click();
-
- // Back to home
- mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
- assertThat(mDevice.findObject(By.text("Skip anyway"))).isNull();
- assertThat(mDevice.findObject(By.text(DO_IT_LATER))).isNull();
- }
-
- @Test
- public void testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchIntroWithGkPwHandle(true);
-
- // Intro page
- verifyIntroPage();
- final UiObject2 agreeBtn = mDevice.findObject(By.text("I agree"));
- assertThat(agreeBtn).isNotNull();
- agreeBtn.click();
-
- // FindSensor page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
- assertThat(doItLaterBtn).isNotNull();
- assertThat(doItLaterBtn.isClickable()).isTrue();
- doItLaterBtn.click();
-
- // SkipSetupFindFpsDialog
- assertThat(mDevice.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT))
- .isTrue();
- final UiObject2 goBackBtn = mDevice.findObject(By.text("Go back"));
- assertThat(goBackBtn).isNotNull();
- assertThat(goBackBtn.isClickable()).isTrue();
- goBackBtn.click();
-
- // FindSensor page again
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testIntroCheckPin() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- final Intent intent = newActivityIntent(false);
- mContext.startActivity(intent);
- assertThat(mDevice.wait(Until.hasObject(By.text("Enter your device PIN to continue")),
- IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testEnrollingWithGkPwHandle() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchEnrollingWithGkPwHandle();
-
- // Enrolling screen
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testEnrollingIconTouchDialog_withSfps() {
- assumeTrue(mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchEnrollingWithGkPwHandle();
-
- // Enrolling screen
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
-
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
-
- lottie.click();
- lottie.click();
- lottie.click();
-
- // IconTouchDialog
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text("Touch the sensor instead")), IDLE_TIMEOUT))
- .isTrue();
- final UiObject2 okButton = mDevice.findObject(By.text("OK"));
- assertThat(okButton).isNotNull();
-
- okButton.click();
-
- // Enrolling screen again
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testEnrollingIconTouchDialog_withRfps() {
- assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchEnrollingWithGkPwHandle();
-
- // Enrolling screen
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
-
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "fingerprint_progress_bar"));
- assertThat(lottie).isNotNull();
-
- lottie.click();
- lottie.click();
- lottie.click();
-
- // IconTouchDialog
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
- IDLE_TIMEOUT)).isTrue();
- final UiObject2 okButton = mDevice.findObject(By.text("OK"));
- assertThat(okButton).isNotNull();
-
- okButton.click();
-
- // Enrolling screen again
- mDevice.waitForIdle();
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testFindUdfpsWithGkPwHandle_clickStart() {
- assumeTrue(mCanAssumeUdfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchFindSensorWithGkPwHandle();
-
- // FindUdfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
- assertThat(lottie.isClickable()).isTrue();
- final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
- assertThat(startBtn.isClickable()).isTrue();
- startBtn.click();
-
- // Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testFindUdfpsWithGkPwHandle_clickLottie() {
- assumeTrue(mCanAssumeUdfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchFindSensorWithGkPwHandle();
-
- // FindUdfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
- assertThat(lottie.isClickable()).isTrue();
- final UiObject2 startBtn = mDevice.findObject(By.text("Start"));
- assertThat(startBtn.isClickable()).isTrue();
- lottie.click();
-
- // Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
- }
-
- @Test
- public void testFindSfpsWithGkPwHandle() {
- assumeTrue(mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchFindSensorWithGkPwHandle();
-
- // FindSfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- assertThat(lottie).isNotNull();
-
- // We don't have view which can be clicked to run to next page, stop at here.
- }
-
- @Test
- public void testFindRfpsWithGkPwHandle() {
- assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
-
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchFindSensorWithGkPwHandle();
-
- // FindRfps page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "illustration_lottie"));
- if (lottie == null) {
- // FindSfps page shall have an animation view if no lottie view
- assertThat(mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
- "fingerprint_sensor_location_animation"))).isNotNull();
- }
- }
-
-
- @Test
- public void testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
- LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
-
- launchFindSensorWithGkPwHandle();
-
- // FindSensor page
- assertThat(mDevice.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- final UiObject2 doItLaterBtn = mDevice.findObject(By.text(DO_IT_LATER));
- assertThat(doItLaterBtn).isNotNull();
- assertThat(doItLaterBtn.isClickable()).isTrue();
- doItLaterBtn.click();
-
- // Back to home
- mDevice.waitForWindowUpdate("com.android.settings", IDLE_TIMEOUT);
- assertThat(mDevice.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue();
- }
-
- private void launchIntroWithGkPwHandle(boolean isSuw) {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
- final int userId = UserHandle.myUserId();
- final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
- final Intent intent = newActivityIntent(isSuw);
- intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
- mContext.startActivity(intent);
- };
- LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
- userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
- }
-
- private void launchFindSensorWithGkPwHandle() {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
- final int userId = UserHandle.myUserId();
- final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
- final Intent intent = newActivityIntent(false);
- intent.putExtra(EXTRA_SKIP_INTRO, true);
- intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
- mContext.startActivity(intent);
- };
- LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
- userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
- }
-
- private void launchEnrollingWithGkPwHandle() {
- LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
- final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
- final int userId = UserHandle.myUserId();
- final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
- final Intent intent = newActivityIntent(false);
- intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true);
- intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
- mContext.startActivity(intent);
- };
- LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
- userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
- }
-
- @NonNull
- private Intent newActivityIntent(boolean isSuw) {
- Intent intent = new Intent();
- intent.setClassName(SETTINGS_PACKAGE_NAME,
- isSuw ? SUW_ACTIVITY_CLASS_NAME : ACTIVITY_CLASS_NAME);
- if (isSuw) {
- intent.putExtra(EXTRA_IS_SETUP_FLOW, true);
- }
- intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1);
- intent.putExtra(Intent.EXTRA_USER_ID, mContext.getUserId());
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-}
diff --git a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt
new file mode 100644
index 0000000..49c5ac9
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt
@@ -0,0 +1,632 @@
+/*
+ * 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.biometrics2.ui.view
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager.FEATURE_FINGERPRINT
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import android.os.UserHandle
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiObject2
+import android.support.test.uiautomator.Until
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.widget.LockPatternChecker
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.android.internal.widget.VerifyCredentialResponse
+import com.android.settings.biometrics2.utils.LockScreenUtil
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class FingerprintEnrollmentActivityTest {
+
+ private val context: Context by lazy {
+ InstrumentationRegistry.getInstrumentation().context
+ }
+
+ private val fingerprintManager: FingerprintManager by lazy {
+ context.getSystemService(FingerprintManager::class.java)!!
+ }
+
+ private var fingerprintPropCallbackLaunched = false
+ private var canAssumeUdfps = false
+ private var canAssumeSfps = false
+ private var enrollingPageTitle: String = ""
+
+ private val device: UiDevice by lazy {
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ }
+
+ @Before
+ @Throws(InterruptedException::class)
+ fun setUp() {
+ // Stop every test if it is not a fingerprint device
+ Assume.assumeTrue(context.packageManager.hasSystemFeature(FEATURE_FINGERPRINT))
+
+ fingerprintPropCallbackLaunched = false
+ fingerprintManager.addAuthenticatorsRegisteredCallback(
+ object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ override fun onAllAuthenticatorsRegistered(
+ list: List<FingerprintSensorPropertiesInternal>
+ ) {
+ fingerprintPropCallbackLaunched = true
+ assertThat(list).isNotNull()
+ assertThat(list).isNotEmpty()
+ val prop = list[0]
+ canAssumeUdfps = prop.isAnyUdfpsType
+ canAssumeSfps = prop.isAnySidefpsType
+ enrollingPageTitle = if (canAssumeUdfps) {
+ UDFPS_ENROLLING_TITLE
+ } else if (canAssumeSfps) {
+ SFPS_ENROLLING_TITLE
+ } else {
+ RFPS_ENROLLING_TITLE
+ }
+ }
+ })
+ var i: Long = 0
+ while (i < IDLE_TIMEOUT && !fingerprintPropCallbackLaunched) {
+ Thread.sleep(100L)
+ i += 100L
+ }
+ assertThat(fingerprintPropCallbackLaunched).isTrue()
+ device.pressHome()
+
+ // Stop settings before performing test
+ try {
+ device.executeShellCommand("am force-stop $SETTINGS_PACKAGE_NAME")
+ } catch (e: IOException) {
+ Log.e(TAG, "Fail to stop settings app", e)
+ }
+ }
+
+ @After
+ @Throws(Exception::class)
+ fun tearDown() {
+ LockScreenUtil.resetLockscreen(TEST_PIN)
+ device.pressHome()
+ }
+
+ @Test
+ fun testIntroChooseLock() {
+ val intent = newActivityIntent(false)
+ context.startActivity(intent)
+ assertThat(
+ device.wait(
+ Until.hasObject(By.text("Choose your backup screen lock method")),
+ IDLE_TIMEOUT
+ )
+ ).isTrue()
+ }
+
+ private fun verifyIntroPage() {
+ device.waitForIdle()
+ run {
+ var i: Long = 0
+ while (i < IDLE_TIMEOUT) {
+ if (device.wait(Until.hasObject(By.text("More")), 50L)) {
+ break
+ } else if (device.wait(Until.hasObject(By.text("I agree")), 50L)) {
+ break
+ }
+ i += 100L
+ }
+ }
+
+ // Click more btn at most twice and the introduction should stay in the last page
+ var moreBtn: UiObject2? = null
+ var i = 0
+ while (i < 2 && device.findObject(By.text("More")).also { moreBtn = it } != null) {
+ moreBtn!!.click()
+ device.waitForIdle()
+ device.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT)
+ ++i
+ }
+ assertThat(device.wait(Until.hasObject(By.text("No thanks")), IDLE_TIMEOUT)).isTrue()
+ assertThat(device.wait(Until.hasObject(By.text("I agree")), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_withUdfps_clickStart() {
+ Assume.assumeTrue(canAssumeUdfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindUdfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+ assertThat(lottie.isClickable).isTrue()
+ val startBtn = device.findObject(By.text("Start"))
+ assertThat(startBtn.isClickable).isTrue()
+ startBtn.click()
+
+ // Enrolling page
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_withUdfps_clickLottie() {
+ Assume.assumeTrue(canAssumeUdfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindUdfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie"))
+ assertThat(lottie).isNotNull()
+ assertThat(lottie.isClickable).isTrue()
+ val startBtn = device.findObject(By.text("Start"))
+ assertThat(startBtn.isClickable).isTrue()
+ lottie.click()
+
+ // Enrolling page
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_withSfps() {
+ Assume.assumeTrue(canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindSfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME,"illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+
+ // We don't have view which can be clicked to run to next page, stop at here.
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_withRfps() {
+ Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindRfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ if (lottie == null) {
+ // FindSfps page shall have an animation view if no lottie view
+ assertThat(
+ device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "fingerprint_sensor_location_animation")
+ )
+ ).isNotNull()
+ }
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val noThanksBtn = device.findObject(By.text("No thanks"))
+ assertThat(noThanksBtn).isNotNull()
+ noThanksBtn.click()
+
+ // Back to home
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
+ assertThat(device.findObject(By.text("No thanks"))).isNull()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_clickSkipInFindSensor() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(false)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindSensor page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
+ assertThat(doItLaterBtn).isNotNull()
+ assertThat(doItLaterBtn.isClickable).isTrue()
+ doItLaterBtn.click()
+
+ // Back to home
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
+ assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(true)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindSensor page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
+ assertThat(doItLaterBtn).isNotNull()
+ assertThat(doItLaterBtn.isClickable).isTrue()
+ doItLaterBtn.click()
+
+ // SkipSetupFindFpsDialog
+ assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
+ val skipAnywayBtn = device.findObject(By.text("Skip anyway"))
+ assertThat(skipAnywayBtn).isNotNull()
+ assertThat(skipAnywayBtn.isClickable).isTrue()
+ skipAnywayBtn.click()
+
+ // Back to home
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
+ assertThat(device.findObject(By.text("Skip anyway"))).isNull()
+ assertThat(device.findObject(By.text(DO_IT_LATER))).isNull()
+ }
+
+ @Test
+ fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchIntroWithGkPwHandle(true)
+
+ // Intro page
+ verifyIntroPage()
+ val agreeBtn = device.findObject(By.text("I agree"))
+ assertThat(agreeBtn).isNotNull()
+ agreeBtn.click()
+
+ // FindSensor page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
+ assertThat(doItLaterBtn).isNotNull()
+ assertThat(doItLaterBtn.isClickable).isTrue()
+ doItLaterBtn.click()
+
+ // SkipSetupFindFpsDialog
+ assertThat(device.wait(Until.hasObject(By.text("Skip fingerprint?")), IDLE_TIMEOUT)).isTrue()
+ val goBackBtn = device.findObject(By.text("Go back"))
+ assertThat(goBackBtn).isNotNull()
+ assertThat(goBackBtn.isClickable).isTrue()
+ goBackBtn.click()
+
+ // FindSensor page again
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testIntroCheckPin() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ val intent = newActivityIntent(false)
+ context.startActivity(intent)
+ assertThat(
+ device.wait(
+ Until.hasObject(By.text("Enter your device PIN to continue")),
+ IDLE_TIMEOUT
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun testEnrollingWithGkPwHandle() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchEnrollingWithGkPwHandle()
+
+ // Enrolling screen
+ device.waitForIdle()
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testEnrollingIconTouchDialog_withSfps() {
+ Assume.assumeTrue(canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchEnrollingWithGkPwHandle()
+
+ // Enrolling screen
+ device.waitForIdle()
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+ lottie.click()
+ lottie.click()
+ lottie.click()
+
+ // IconTouchDialog
+ device.waitForIdle()
+ assertThat(
+ device.wait(
+ Until.hasObject(By.text("Touch the sensor instead")),
+ IDLE_TIMEOUT
+ )
+ )
+ .isTrue()
+ val okButton = device.findObject(By.text("OK"))
+ assertThat(okButton).isNotNull()
+ okButton.click()
+
+ // Enrolling screen again
+ device.waitForIdle()
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testEnrollingIconTouchDialog_withRfps() {
+ Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchEnrollingWithGkPwHandle()
+
+ // Enrolling screen
+ device.waitForIdle()
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "fingerprint_progress_bar")
+ )
+ assertThat(lottie).isNotNull()
+ lottie.click()
+ lottie.click()
+ lottie.click()
+
+ // IconTouchDialog
+ device.waitForIdle()
+ assertThat(
+ device.wait(
+ Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
+ IDLE_TIMEOUT
+ )
+ ).isTrue()
+ val okButton = device.findObject(By.text("OK"))
+ assertThat(okButton).isNotNull()
+ okButton.click()
+
+ // Enrolling screen again
+ device.waitForIdle()
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testFindUdfpsWithGkPwHandle_clickStart() {
+ Assume.assumeTrue(canAssumeUdfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchFindSensorWithGkPwHandle()
+
+ // FindUdfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+ assertThat(lottie.isClickable).isTrue()
+ val startBtn = device.findObject(By.text("Start"))
+ assertThat(startBtn.isClickable).isTrue()
+ startBtn.click()
+
+ // Enrolling page
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testFindUdfpsWithGkPwHandle_clickLottie() {
+ Assume.assumeTrue(canAssumeUdfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchFindSensorWithGkPwHandle()
+
+ // FindUdfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+ assertThat(lottie.isClickable).isTrue()
+ val startBtn = device.findObject(By.text("Start"))
+ assertThat(startBtn.isClickable).isTrue()
+ lottie.click()
+
+ // Enrolling page
+ assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ @Test
+ fun testFindSfpsWithGkPwHandle() {
+ Assume.assumeTrue(canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchFindSensorWithGkPwHandle()
+
+ // FindSfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+ )
+ assertThat(lottie).isNotNull()
+
+ // We don't have view which can be clicked to run to next page, stop at here.
+ }
+
+ @Test
+ fun testFindRfpsWithGkPwHandle() {
+ Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchFindSensorWithGkPwHandle()
+
+ // FindRfps page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val lottie = device.findObject(
+ By.res(
+ SETTINGS_PACKAGE_NAME,
+ "illustration_lottie"
+ )
+ )
+ if (lottie == null) {
+ // FindSfps page shall have an animation view if no lottie view
+ assertThat(
+ device.findObject(
+ By.res(
+ SETTINGS_PACKAGE_NAME,
+ "fingerprint_sensor_location_animation"
+ )
+ )
+ ).isNotNull()
+ }
+ }
+
+ @Test
+ fun testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+ launchFindSensorWithGkPwHandle()
+
+ // FindSensor page
+ assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ val doItLaterBtn = device.findObject(By.text(DO_IT_LATER))
+ assertThat(doItLaterBtn).isNotNull()
+ assertThat(doItLaterBtn.isClickable).isTrue()
+ doItLaterBtn.click()
+
+ // Back to home
+ device.waitForWindowUpdate(SETTINGS_PACKAGE_NAME, IDLE_TIMEOUT)
+ assertThat(device.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+ }
+
+ private fun launchIntroWithGkPwHandle(isSuw: Boolean) {
+ val lockPatternUtils = LockPatternUtils(context)
+ val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
+ val userId = UserHandle.myUserId()
+ val onVerifyCallback =
+ LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
+ val intent = newActivityIntent(isSuw)
+ intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
+ context.startActivity(intent)
+ }
+ LockPatternChecker.verifyCredential(
+ lockPatternUtils, lockscreenCredential,
+ userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
+ )
+ }
+
+ private fun launchFindSensorWithGkPwHandle() {
+ val lockPatternUtils = LockPatternUtils(context)
+ val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
+ val userId = UserHandle.myUserId()
+ val onVerifyCallback =
+ LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
+ val intent = newActivityIntent(false)
+ intent.putExtra(EXTRA_SKIP_INTRO, true)
+ intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
+ context.startActivity(intent)
+ }
+ LockPatternChecker.verifyCredential(
+ lockPatternUtils, lockscreenCredential,
+ userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
+ )
+ }
+
+ private fun launchEnrollingWithGkPwHandle() {
+ val lockPatternUtils = LockPatternUtils(context)
+ val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
+ val userId = UserHandle.myUserId()
+ val onVerifyCallback =
+ LockPatternChecker.OnVerifyCallback { response: VerifyCredentialResponse, _: Int ->
+ val intent = newActivityIntent(false)
+ intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true)
+ intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.gatekeeperPasswordHandle)
+ context.startActivity(intent)
+ }
+ LockPatternChecker.verifyCredential(
+ lockPatternUtils, lockscreenCredential,
+ userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback
+ )
+ }
+
+ private fun newActivityIntent(isSuw: Boolean): Intent {
+ val intent = Intent()
+ intent.setClassName(
+ SETTINGS_PACKAGE_NAME,
+ if (isSuw) SUW_ACTIVITY_CLASS_NAME else ACTIVITY_CLASS_NAME
+ )
+ if (isSuw) {
+ intent.putExtra(EXTRA_IS_SETUP_FLOW, true)
+ }
+ intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1)
+ intent.putExtra(Intent.EXTRA_USER_ID, context.userId)
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ companion object {
+ private const val TAG = "FingerprintEnrollmentActivityTest"
+ const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+ private const val ACTIVITY_CLASS_NAME =
+ "com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity"
+ private const val SUW_ACTIVITY_CLASS_NAME = "$ACTIVITY_CLASS_NAME\$SetupActivity"
+ private const val EXTRA_IS_SETUP_FLOW = "isSetupFlow"
+ private const val EXTRA_SKIP_INTRO = "skip_intro"
+ private const val EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor"
+ private const val EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"
+ private const val EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"
+ private const val TEST_PIN = "1234"
+ private const val DO_IT_LATER = "Do it later"
+ private const val UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor"
+ private const val SFPS_ENROLLING_TITLE =
+ "Lift, then touch. Move your finger slightly each time."
+ private const val RFPS_ENROLLING_TITLE = "Lift, then touch again"
+ private const val IDLE_TIMEOUT = 10000L
+ }
+}