/*
 * 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.Lifecycle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
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.factory.BiometricsViewModelFactory
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CREDENTIAL_MODEL_KEY
import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_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.FingerprintChallengeGenerator
import com.android.settings.biometrics2.ui.viewmodel.CredentialAction
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
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.FingerprintEnrollEnrollingAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
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.FingerprintEnrollIntroAction
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.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.google.android.setupdesign.util.ThemeHelper
import kotlinx.coroutines.launch

/**
 * Fingerprint enrollment activity implementation
 */
open class FingerprintEnrollmentActivity : FragmentActivity() {
    /** SetupWizard activity*/
    class SetupActivity : FingerprintEnrollmentActivity()

    /** Internal activity for FingerprintSettings */
    class InternalActivity : FingerprintEnrollmentActivity()

    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 introViewModel: FingerprintEnrollIntroViewModel by lazy {
        viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
    }

    private val findSensorViewModel: FingerprintEnrollFindSensorViewModel by lazy {
        viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
    }

    private val progressViewModel: FingerprintEnrollProgressViewModel by lazy {
        viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
    }

    private val enrollingViewModel: FingerprintEnrollEnrollingViewModel by lazy {
        viewModelProvider[FingerprintEnrollEnrollingViewModel::class.java]
    }

    private val finishViewModel: FingerprintEnrollFinishViewModel by lazy {
        viewModelProvider[FingerprintEnrollFinishViewModel::class.java]
    }

    private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel by lazy {
        viewModelProvider[FingerprintEnrollErrorDialogViewModel::class.java]
    }

    private var isFirstFragmentAdded = false

    private val findSensorActionObserver = Observer<Int?> { action ->
        if (DEBUG) {
            Log.d(TAG, "findSensorActionObserver($action)")
        }
        action?.let { onFindSensorAction(it) }
    }

    private val enrollingActionObserver = Observer<Int?> { action ->
        if (DEBUG) {
            Log.d(TAG, "enrollingActionObserver($action)")
        }
        action?.let { onEnrollingAction(it) }
    }

    private val finishActionObserver = 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)

        // 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
        )
        Log.d(
            TAG,
            "onCreate() has savedInstance:$(savedInstanceState != null), fragment:$fragment"
        )

        isFirstFragmentAdded = (savedInstanceState != null)
        if (fragment == null) {
            checkCredential()
            if (viewModel.request.isSkipFindSensor) {
                startEnrollingFragment()
            } else if (viewModel.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
            }
        }

        collectFlows()
    }

    private fun collectFlows() {
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.setResultFlow.collect {
                    Log.d(TAG, "setResultLiveData($it)")
                    onSetActivityResult(it)
                }
            }
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                autoCredentialViewModel.generateChallengeFailedFlow.collect {
                    Log.d(TAG, "generateChallengeFailedFlow($it)")
                    onSetActivityResult(ActivityResult(RESULT_CANCELED, null))
                }
            }
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                errorDialogViewModel.newDialogFlow.collect {
                    Log.d(TAG, "newErrorDialogFlow($it)")
                    FingerprintEnrollErrorDialog.newInstance(it).show(
                        supportFragmentManager,
                        ERROR_DIALOG_TAG
                    )
                }
            }
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                errorDialogViewModel.setResultFlow.collect {
                    Log.d(TAG, "errorDialogSetResultFlow($it)")
                    when (it) {
                        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 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(
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_enter_dynamic_color,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_exit,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_close_enter_dynamic_color,
                    com.google.android.setupdesign.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
        }
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                introViewModel.actionFlow.collect(this@FingerprintEnrollmentActivity::onIntroAction)
            }
        }
    }

    // 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
        progressViewModel.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
        }
        findSensorViewModel.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
        progressViewModel.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() {
        enrollingViewModel.let {
            it.clearActionLiveData()
            it.actionLiveData.observe(this, enrollingActionObserver)
        }
    }

    private fun startFinishFragment() {
        viewModel.isNewFingerprintAdded = true
        attachFinishViewModel()
        if (viewModel.request.isSkipFindSensor) {
            // Set page to Finish
            supportFragmentManager.beginTransaction()
                .setReorderingAllowed(true)
                .setCustomAnimations(
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_enter_dynamic_color,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_exit,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_close_enter_dynamic_color,
                    com.google.android.setupdesign.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(
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_enter_dynamic_color,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_open_exit,
                    com.google.android.setupdesign.R.anim.shared_x_axis_activity_close_enter_dynamic_color,
                    com.google.android.setupdesign.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() {
        finishViewModel.let {
            it.clearActionLiveData()
            it.actionLiveData.observe(this, finishActionObserver)
        }
    }

    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(lifecycleScope)) {
            CredentialAction.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
            }

            CredentialAction.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
            }

            CredentialAction.CREDENTIAL_VALID,
            CredentialAction.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.generateChallengeAsCredentialActivityResult(
                isChooseLock,
                activityResult,
                lifecycleScope
            )
        ) {
            onSetActivityResult(activityResult)
        }
    }

    private fun onIntroAction(action: FingerprintEnrollIntroAction) {
        Log.d(TAG, "onIntroAction($action)")
        when (action) {
            FingerprintEnrollIntroAction.DONE_AND_FINISH -> {
                onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null))
                return
            }

            FingerprintEnrollIntroAction.SKIP_OR_CANCEL -> {
                onSetActivityResult(ActivityResult(BiometricEnrollBase.RESULT_SKIP, null))
                return
            }

            FingerprintEnrollIntroAction.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 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,
            lifecycleScope
        )
    }

    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() = MutableCreationExtras(super.defaultViewModelCreationExtras).also {
            it[CHALLENGE_GENERATOR_KEY] = FingerprintChallengeGenerator(
                featureFactory.biometricsRepositoryProvider.getFingerprintRepository(application)!!
            )
            it[ENROLLMENT_REQUEST_KEY] =
                EnrollmentRequest(intent, applicationContext, this is SetupActivity)
            it[CREDENTIAL_MODEL_KEY] =
                CredentialModel(intent.extras, SystemClock.elapsedRealtimeClock())
        }

    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)
    }

    companion object {
        private const val DEBUG = false
        private const val TAG = "FingerprintEnrollmentActivity"
        protected const val LAUNCH_CONFIRM_LOCK_ACTIVITY = 1

        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 ERROR_DIALOG_TAG = "error-dialog"
    }
}