[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
+    }
+}