Adding FingerprintEnrollConfirmation

Bug: 295217067
Test: atest FingerprintEnrollConfirmationScreenshotTest.kt
Test: adb shell device_config put biometrics_framework com.android.settings.flags.fingerprint_v2_enrollment true
Change-Id: I0b4995a97b7bccd35e26ed89bbbf14b80af4b0e1
diff --git a/aconfig/settings_biometrics_framework_flag_declarations.aconfig b/aconfig/settings_biometrics_framework_flag_declarations.aconfig
index e9f19bc..4355ed1 100644
--- a/aconfig/settings_biometrics_framework_flag_declarations.aconfig
+++ b/aconfig/settings_biometrics_framework_flag_declarations.aconfig
@@ -7,3 +7,10 @@
   description: "This flag enables or disables the BiometricSettingsProvider"
   bug: "303595205"
 }
+
+flag {
+  name: "fingerprint_v2_enrollment"
+  namespace: "biometrics_framework"
+  description: "This flag enables or disables the Fingerprint v2 enrollment"
+  bug: "295206723"
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index 65030de..d26b812 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -37,13 +37,13 @@
 import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
 import com.android.settings.biometrics.GatekeeperPasswordProvider
 import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
-import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
 import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
 import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
 import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
+import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
 import com.android.settings.biometrics.fingerprint2.lib.model.Default
 import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
@@ -54,6 +54,7 @@
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
@@ -70,6 +71,7 @@
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
+import com.android.settings.flags.Flags
 import com.android.settings.password.ChooseLockGeneric
 import com.android.settings.password.ChooseLockSettingsHelper
 import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
@@ -96,6 +98,8 @@
   private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
   private lateinit var backgroundViewModel: BackgroundViewModel
   private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
+  private lateinit var fingerprintEnrollConfirmationViewModel:
+    FingerprintEnrollConfirmationViewModel
   private val coroutineDispatcher = Dispatchers.Default
 
   /** Result listener for ChooseLock activity flow. */
@@ -155,6 +159,15 @@
     // TODO(b/299573056): Show split screen dialog when it's in multi window mode.
     setContentView(R.layout.fingerprint_v2_enroll_main)
 
+    if (!Flags.fingerprintV2Enrollment()) {
+      check(false) {
+        "fingerprint enrollment v2 is not enabled, " +
+          "please run adb shell device_config put " +
+          "biometrics_framework com.android.settings.flags.fingerprint_v2_enrollment true"
+      }
+      finish()
+    }
+
     setTheme(SetupWizardUtils.getTheme(applicationContext, intent))
     ThemeHelper.trySetDynamicColor(applicationContext)
 
@@ -293,6 +306,15 @@
       ),
     )[RFPSViewModel::class.java]
 
+    fingerprintEnrollConfirmationViewModel =
+      ViewModelProvider(
+        this,
+        FingerprintEnrollConfirmationViewModel.FingerprintEnrollConfirmationViewModelFactory(
+          navigationViewModel,
+          fingerprintManagerInteractor,
+        ),
+      )[FingerprintEnrollConfirmationViewModel::class.java]
+
     lifecycleScope.launch {
       navigationViewModel.currentStep.collect { step ->
         if (step is Init) {
@@ -304,6 +326,7 @@
 
     lifecycleScope.launch {
       navigationViewModel.navigateTo.filterNotNull().collect { step ->
+        Log.d(TAG, "navigateTo: $step")
         if (step is ConfirmDeviceCredential) {
           launchConfirmOrChooseLock(userId)
           navigationViewModel.update(
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
index fd07a95..d8c1c93 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
@@ -17,7 +17,21 @@
 package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
 
 import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.VisibleForTesting
 import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
+import com.google.android.setupcompat.template.FooterBarMixin
+import com.google.android.setupcompat.template.FooterButton
+import com.google.android.setupdesign.GlifLayout
+import kotlinx.coroutines.launch
 
 /**
  * A fragment to indicate that fingerprint enrollment has been completed.
@@ -25,9 +39,71 @@
  * This page will display basic information about what a fingerprint can be used for and acts as the
  * final step of enrollment.
  */
-class FingerprintEnrollConfirmationV2Fragment : Fragment() {
+class FingerprintEnrollConfirmationV2Fragment() :
+  Fragment(R.layout.fingerprint_enroll_finish_base) {
 
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
+  companion object {
+    const val TAG = "FingerprintEnrollConfirmationV2Fragment"
+  }
+
+  /** Used for testing purposes */
+  private var factory: ViewModelProvider.Factory? = null
+
+  @VisibleForTesting
+  constructor(theFactory: ViewModelProvider.Factory) : this() {
+    factory = theFactory
+  }
+
+  private val viewModelProvider: ViewModelProvider by lazy {
+    if (factory != null) {
+      ViewModelProvider(requireActivity(), factory!!)
+    } else {
+      ViewModelProvider(requireActivity())
+    }
+  }
+
+  private val viewModel: FingerprintEnrollConfirmationViewModel by lazy {
+    viewModelProvider[FingerprintEnrollConfirmationViewModel::class.java]
+  }
+
+  override fun onCreateView(
+    inflater: LayoutInflater,
+    container: ViewGroup?,
+    savedInstanceState: Bundle?,
+  ): View? =
+    super.onCreateView(inflater, container, savedInstanceState).also { theView ->
+      val mainView = theView!! as GlifLayout
+
+      mainView.setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title)
+      mainView.setDescriptionText(R.string.security_settings_fingerprint_enroll_finish_v2_message)
+
+      val mixin = mainView.getMixin(FooterBarMixin::class.java)
+      viewLifecycleOwner.lifecycleScope.launch {
+        repeatOnLifecycle(Lifecycle.State.RESUMED) {
+          viewModel.isAddAnotherButtonVisible.collect {
+            mixin.secondaryButton =
+              FooterButton.Builder(requireContext())
+                .setText(R.string.fingerprint_enroll_button_add)
+                .setListener { viewModel.onAddAnotherButtonClicked() }
+                .setButtonType(FooterButton.ButtonType.SKIP)
+                .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
+                .build()
+          }
+        }
+      }
+
+      mixin.setPrimaryButton(
+        FooterButton.Builder(requireContext())
+          .setText(R.string.security_settings_fingerprint_enroll_done)
+          .setListener(this::onNextButtonClick)
+          .setButtonType(FooterButton.ButtonType.NEXT)
+          .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+          .build()
+      )
+    }
+
+  @Suppress("UNUSED_PARAMETER")
+  private fun onNextButtonClick(view: View?) {
+    viewModel.onNextButtonClicked()
   }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
index 2408a88..6ee5709 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt
@@ -47,10 +47,12 @@
   /** Value to indicate if the text view is visible or not */
   val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
 
+  private var _shouldAnimateIcon: Flow<Boolean> =
+    fingerprintEnrollViewModel.enrollFlowShouldBeRunning
   /** Indicates if the icon should be animating or not */
-  val shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
+  val shouldAnimateIcon = _shouldAnimateIcon
 
-  private val enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFLow
+  private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFLow
 
   /**
    * Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
@@ -59,7 +61,7 @@
   val progress: Flow<FingerEnrollState.EnrollProgress?> =
     enrollFlow
       .filterIsInstance<FingerEnrollState.EnrollProgress>()
-      .shareIn(viewModelScope, SharingStarted.Eagerly, 1)
+      .shareIn(viewModelScope, SharingStarted.Eagerly, 0)
 
   /** Clear help message on enroll progress */
   val clearHelpMessage: Flow<Boolean> = progress.map { it != null }
@@ -122,6 +124,7 @@
 
   /** Indicates the negative button has been clicked */
   fun negativeButtonClicked() {
+    doReset()
     navigationViewModel.update(
       FingerprintAction.NEGATIVE_BUTTON_PRESSED,
       navStep,
@@ -129,11 +132,19 @@
     )
   }
 
-  /** Indicates that enrollment has been finished and we can proceed to the next step. */
+  /** Indicates that an enrollment was completed */
   fun finishedSuccessfully() {
+    doReset()
     navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished")
   }
 
+  private fun doReset() {
+    _textViewIsVisible.update { false }
+    _shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
+    /** Indicates if the icon should be animating or not */
+    enrollFlow = fingerprintEnrollViewModel.enrollFLow
+  }
+
   class RFPSViewModelFactory(
     private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
     private val navigationViewModel: FingerprintNavigationViewModel,
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt
new file mode 100644
index 0000000..d9b31d7
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Models the UI state for [FingerprintEnrollConfirmationV2Fragment]
+ */
+class FingerprintEnrollConfirmationViewModel(
+  private val navigationViewModel: FingerprintNavigationViewModel,
+  fingerprintInteractor: FingerprintManagerInteractor,
+) : ViewModel() {
+
+  /**
+   * Indicates if the add another button is possible. This should only be true when the user is able
+   * to enroll more fingerprints.
+   */
+  val isAddAnotherButtonVisible: Flow<Boolean> = fingerprintInteractor.canEnrollFingerprints
+
+  /**
+   * Indicates that the user has clicked the next button and is done with fingerprint enrollment.
+   */
+  fun onNextButtonClicked() {
+    navigationViewModel.update(FingerprintAction.NEXT, navStep, "onNextButtonClicked")
+  }
+
+  /**
+   * Indicates that the user has clicked the add another button and will be sent to the enrollment
+   * screen.
+   */
+  fun onAddAnotherButtonClicked() {
+    navigationViewModel.update(FingerprintAction.ADD_ANOTHER, navStep, "onAddAnotherButtonClicked")
+  }
+
+  class FingerprintEnrollConfirmationViewModelFactory(
+    private val navigationViewModel: FingerprintNavigationViewModel,
+    private val fingerprintInteractor: FingerprintManagerInteractor,
+  ) : ViewModelProvider.Factory {
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+      return FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintInteractor) as T
+    }
+  }
+
+  companion object {
+    private const val TAG = "FingerprintEnrollConfirmationViewModel"
+    private val navStep = FingerprintNavigationStep.Confirmation::class
+  }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt
index 979f953..76b4895 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt
@@ -37,6 +37,7 @@
   ACTIVITY_CREATED,
   NEGATIVE_BUTTON_PRESSED,
   USER_CLICKED_FINISH,
+  ADD_ANOTHER,
 }
 
 /** State that can be used to help a [FingerprintNavigationStep] determine the next step to take. */
@@ -179,6 +180,7 @@
       return when (action) {
         FingerprintAction.NEXT -> Finish(null)
         FingerprintAction.PREV -> TransitionStep(Education(state.fingerprintSensor!!))
+        FingerprintAction.ADD_ANOTHER -> TransitionStep(Enrollment(state.fingerprintSensor!!))
         else -> null
       }
     }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
index 26c20cf..131f5bb 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt
@@ -89,7 +89,7 @@
   fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) {
     Log.d(TAG, "$caller.update($action) $debugStr")
     val currentStep = _currentStep.value
-    val isUiStep = currentStep is UiStep
+    val isUiStep = currentStep is UiStep && caller is UiStep
     if (currentStep == null) {
       throw NullPointerException("current step is null")
     }
diff --git a/tests/screenshot/OWNERS b/tests/screenshot/OWNERS
new file mode 100644
index 0000000..fbe29d1
--- /dev/null
+++ b/tests/screenshot/OWNERS
@@ -0,0 +1,2 @@
+joshmccloskey@google.com
+jbolinger@google.com
diff --git a/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png b/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png
new file mode 100644
index 0000000..db9009b
--- /dev/null
+++ b/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png
Binary files differ
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
index 84d76ff..3cd2002 100644
--- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
@@ -27,6 +27,7 @@
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
@@ -103,6 +104,9 @@
   var rfpsViewModel =
     RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor)
 
+  val fingerprintEnrollConfirmationViewModel =
+    FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
+
   var fingerprintFindSensorViewModel =
     FingerprintEnrollFindSensorViewModel(
       navigationViewModel,
@@ -131,6 +135,7 @@
           BackgroundViewModel::class.java -> backgroundViewModel
           RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
           FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
+          FingerprintEnrollConfirmationViewModel::class.java -> fingerprintEnrollConfirmationViewModel
           else -> null
         }
           as T
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt
new file mode 100644
index 0000000..28f4fbe
--- /dev/null
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.tests.screenshot.biometrics.fingerprint.fragment
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
+import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.screenshot.FragmentScreenshotTestRule
+import platform.test.screenshot.ViewScreenshotTestRule.Mode
+
+@RunWith(AndroidJUnit4::class)
+class FingerprintEnrollConfirmationScreenshotTest {
+  private val injector: Injector = Injector(FingerprintNavigationStep.Confirmation)
+
+  @Rule
+  @JvmField
+  var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
+
+  @Test
+  fun testConfirmation() {
+    rule.screenshotTest(
+      "fp_enroll_confirmation",
+      Mode.MatchSize,
+      FingerprintEnrollConfirmationV2Fragment(injector.factory),
+    )
+  }
+}
diff --git a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
index f202c4f..829716e 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
@@ -40,6 +40,7 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
@@ -119,21 +120,18 @@
       }
     foldStateInteractor =
       object : FoldStateInteractor {
-        override val isFolded: Flow<Boolean> = foldState
+        override val isFolded: Flow<Boolean> = foldState.asStateFlow()
+
         override fun onConfigurationChange(newConfig: Configuration) {
-          TODO("Not yet implemented")
+          foldState.update { false }
         }
       }
+
     orientationInteractor =
-      object: OrientationInteractor {
-        override val orientation: Flow<Int>
-          get() = TODO("Not yet implemented")
+      object : OrientationInteractor {
+        override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
         override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
-
-        override fun getRotationFromDefault(rotation: Int): Int {
-          TODO("Not yet implemented")
-        }
-
+        override fun getRotationFromDefault(rotation: Int): Int = rotation
       }
     underTest =
       FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt
new file mode 100644
index 0000000..696e4fa
--- /dev/null
+++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.viewmodel
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.android.settings.biometrics.fingerprint2.lib.model.Default
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
+import com.android.settings.testutils2.FakeFingerprintManagerInteractor
+import com.android.systemui.biometrics.shared.model.FingerprintSensor
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+@RunWith(MockitoJUnitRunner::class)
+class FingerprintEnrollConfirmationViewModelTest {
+  @JvmField @Rule var rule = MockitoJUnit.rule()
+
+  @get:Rule val instantTaskRule = InstantTaskExecutorRule()
+  private var backgroundDispatcher = StandardTestDispatcher()
+  private var testScope = TestScope(backgroundDispatcher)
+  val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
+  val fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
+  lateinit var navigationViewModel: FingerprintNavigationViewModel
+  lateinit var underTest: FingerprintEnrollConfirmationViewModel
+
+  @Before
+  fun setup() {
+    navigationViewModel =
+      FingerprintNavigationViewModel(
+        FingerprintNavigationStep.Confirmation,
+        false,
+        fingerprintFlowViewModel,
+        fakeFingerprintManagerInteractor,
+      )
+    underTest =
+      FingerprintEnrollConfirmationViewModel(navigationViewModel, fakeFingerprintManagerInteractor)
+  }
+
+  @Test
+  fun testCanEnrollFingerprints() =
+    testScope.runTest {
+      fakeFingerprintManagerInteractor.sensorProp =
+        FingerprintSensor(0 /* sensorId */, SensorStrength.STRONG, 5, FingerprintSensorType.REAR)
+      fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = mutableListOf()
+      fakeFingerprintManagerInteractor.enrollableFingerprints = 5
+
+      var canEnrollFingerprints: Boolean = false
+      val job = launch { underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it } }
+
+      advanceUntilIdle()
+      assertThat(canEnrollFingerprints).isTrue()
+      job.cancel()
+    }
+
+  @Test
+  fun testNextButtonSendsNextStep() =
+    testScope.runTest {
+      var step: FingerprintNavigationStep.UiStep? = null
+      val job = launch { navigationViewModel.navigateTo.collect { step = it } }
+
+      underTest.onNextButtonClicked()
+
+      runCurrent()
+
+      assertThat(step).isNull()
+      job.cancel()
+    }
+
+  @Test
+  fun testAddAnotherSendsAction() =
+    testScope.runTest {
+      var step: FingerprintNavigationStep.UiStep? = null
+      val job = launch { navigationViewModel.navigateTo.collect { step = it } }
+
+      underTest.onAddAnotherButtonClicked()
+
+      runCurrent()
+
+      assertThat(step).isInstanceOf(FingerprintNavigationStep.Enrollment::class.java)
+      job.cancel()
+    }
+}