Added UI tests for FingerprintEnrollIntro

Test: m -j40 RunSettingsRoboTests ROBOTEST_FILTER=FingerprintEnrollmentIntroFragmentTest
Bug: 295206367
Change-Id: I70f6b50dd2604e01805df04ffb1c07a9134ba065
diff --git a/res/layout/fingerprint_v2_enroll_introduction.xml b/res/layout/fingerprint_v2_enroll_introduction.xml
index e9dd08a..2fd1f9c 100644
--- a/res/layout/fingerprint_v2_enroll_introduction.xml
+++ b/res/layout/fingerprint_v2_enroll_introduction.xml
@@ -16,199 +16,210 @@
   -->
 
 <com.google.android.setupdesign.GlifLayout
-xmlns:android="http://schemas.android.com/apk/res/android"
-xmlns:app="http://schemas.android.com/apk/res-auto"
-style="?attr/fingerprint_layout_theme"
-android:id="@+id/setup_wizard_layout"
-android:layout_width="match_parent"
-android:layout_height="match_parent">
-
-<LinearLayout
-    style="@style/SudContentFrame"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    style="?attr/fingerprint_layout_theme"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:clipChildren="false"
-    android:clipToPadding="false"
-    android:orientation="vertical">
+    android:layout_height="match_parent">
 
-    <com.google.android.setupdesign.view.RichTextView
-        android:id="@+id/error_text"
-        style="@style/SudDescription.Glif"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            style="@style/SudContentIllustration"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:contentDescription="@null"
-            android:src="@drawable/fingerprint_enroll_introduction" />
-
-    </FrameLayout>
-
-    <!-- Contains the extra information text at the bottom -->
     <LinearLayout
+        style="@style/SudContentFrame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:orientation="vertical">
 
-        <!-- How it works -->
-        <TextView
+        <com.google.android.setupdesign.view.RichTextView
+            android:id="@+id/error_text"
+            style="@style/SudDescription.Glif"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle"
-            android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_title_2" />
+            android:layout_height="wrap_content" />
 
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                style="@style/SudContentIllustration"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:contentDescription="@null"
+                android:src="@drawable/fingerprint_enroll_introduction" />
+
+        </FrameLayout>
+
+        <!-- Contains the extra information text at the bottom -->
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:orientation="vertical">
 
-            <ImageView
-                android:id="@+id/icon_fingerprint"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_fingerprint_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+            <!-- How it works -->
             <TextView
-                android:id="@+id/footer_message_2"
+                style="@style/BiometricEnrollIntroTitle"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_title_2" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/icon_device_locked"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_lock_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_message_3"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <!-- You're in control -->
-        <TextView
-            android:id="@+id/footer_title_1"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle" />
+                <ImageView
+                    android:id="@+id/icon_fingerprint"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_fingerprint_24dp" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
 
-            <ImageView
-                android:id="@+id/icon_trash_can"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_trash_can"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_message_4"
+                <TextView
+                    android:id="@+id/footer_message_2"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <!-- Keep in mind -->
-        <TextView
-            android:id="@+id/footer_title_2"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle"
-            android:text="@string/security_settings_face_enroll_introduction_info_title"/>
+                <ImageView
+                    android:id="@+id/icon_device_locked"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_lock_24dp" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
 
-            <ImageView
-                android:id="@+id/icon_info"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_info_outline_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+                <TextView
+                    android:id="@+id/footer_message_3"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <!-- You're in control -->
             <TextView
-                android:id="@+id/footer_message_5"
+                android:id="@+id/footer_title_1"
+                style="@style/BiometricEnrollIntroTitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <ImageView
+                    android:id="@+id/icon_trash_can"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_trash_can" />
 
-            <ImageView
-                android:id="@+id/icon_shield"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_guarantee"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_4"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <!-- Keep in mind -->
             <TextView
-                android:id="@+id/footer_message_6"
+                android:id="@+id/footer_title_2"
+                style="@style/BiometricEnrollIntroTitle"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:text="@string/security_settings_face_enroll_introduction_info_title" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/icon_link"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_link_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_learn_more"
-                android:linksClickable="true"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage"
-                android:paddingBottom="0dp"
-                android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" />
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_info"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_info_outline_24dp" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_5"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_shield"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_guarantee" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_6"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_link"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_link_24dp" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_learn_more"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:linksClickable="true"
+                    android:paddingBottom="0dp"
+                    android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" />
+            </LinearLayout>
+
         </LinearLayout>
 
     </LinearLayout>
 
-</LinearLayout>
-
 </com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
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 31afcb7..2565ab8 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
@@ -42,6 +42,7 @@
 import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
 import com.android.settings.biometrics.GatekeeperPasswordProvider
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
+import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
@@ -82,6 +83,7 @@
   private lateinit var accessibilityViewModel: AccessibilityViewModel
   private lateinit var foldStateViewModel: FoldStateViewModel
   private lateinit var orientationStateViewModel: OrientationStateViewModel
+  private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
   private val coroutineDispatcher = Dispatchers.Default
 
   /** Result listener for ChooseLock activity flow. */
@@ -210,8 +212,9 @@
       )[FingerprintEnrollViewModel::class.java]
 
     // Initialize scroll view model
-    ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
-      FingerprintScrollViewModel::class.java]
+    fingerprintScrollViewModel =
+      ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
+        FingerprintScrollViewModel::class.java]
 
     // Initialize AccessibilityViewModel
     accessibilityViewModel =
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
index dbf6d12..898b158 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
@@ -25,10 +25,13 @@
 import android.text.Html
 import android.text.method.LinkMovementMethod
 import android.util.Log
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.ScrollView
 import android.widget.TextView
+import androidx.annotation.VisibleForTesting
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
@@ -72,48 +75,69 @@
  * 2. How the data will be stored
  * 3. How the user can access and remove their data
  */
-class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
-  private lateinit var footerBarMixin: FooterBarMixin
-  private lateinit var textModel: TextModel
-  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
-  private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
-  private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
-  private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
+class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
 
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-    navigationViewModel =
-      ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
-    fingerprintEnrollViewModel =
-      ViewModelProvider(requireActivity())[FingerprintEnrollViewModel::class.java]
-    fingerprintScrollViewModel =
-      ViewModelProvider(requireActivity())[FingerprintScrollViewModel::class.java]
-    gateKeeperViewModel =
-      ViewModelProvider(requireActivity())[FingerprintGatekeeperViewModel::class.java]
+  /** Used for testing purposes */
+  private var factory: ViewModelProvider.Factory? = null
+
+  @VisibleForTesting
+  constructor(theFactory: ViewModelProvider.Factory) : this() {
+    factory = theFactory
   }
 
-  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-    super.onViewCreated(view, savedInstanceState)
+  private val viewModelProvider: ViewModelProvider by lazy {
+    if (factory != null) {
+      ViewModelProvider(requireActivity(), factory!!)
+    } else {
+      ViewModelProvider(requireActivity())
+    }
+  }
 
-    lifecycleScope.launch {
-      combine(
-          navigationViewModel.enrollType,
-          fingerprintEnrollViewModel.sensorType,
-        ) { enrollType, sensorType ->
-          Pair(enrollType, sensorType)
-        }
-        .collect { (enrollType, sensorType) ->
-          textModel =
-            when (enrollType) {
-              Unicorn -> getUnicornTextModel()
-              else -> getNormalTextModel()
-            }
+  private lateinit var footerBarMixin: FooterBarMixin
+  private lateinit var textModel: TextModel
 
-          setupFooterBarAndScrollView(view)
+  // Note that the ViewModels cannot be requested before the onCreate call
+  private val navigationViewModel: FingerprintEnrollNavigationViewModel by lazy {
+    viewModelProvider[FingerprintEnrollNavigationViewModel::class.java]
+  }
+  private val fingerprintViewModel: FingerprintEnrollViewModel by lazy {
+    viewModelProvider[FingerprintEnrollViewModel::class.java]
+  }
+  private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy {
+    viewModelProvider[FingerprintScrollViewModel::class.java]
+  }
+  private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy {
+    viewModelProvider[FingerprintGatekeeperViewModel::class.java]
+  }
 
-          if (savedInstanceState == null) {
-            getLayout()?.setHeaderText(textModel.headerText)
-            getLayout()?.setDescriptionText(textModel.descriptionText)
+  override fun onCreateView(
+    inflater: LayoutInflater,
+    container: ViewGroup?,
+    savedInstanceState: Bundle?
+  ): View? =
+    super.onCreateView(inflater, container, savedInstanceState).also { theView ->
+      val view = theView!!
+
+      viewLifecycleOwner.lifecycleScope.launch {
+        combine(
+            navigationViewModel.enrollType,
+            fingerprintViewModel.sensorType,
+          ) { enrollType, sensorType ->
+            Pair(enrollType, sensorType)
+          }
+          .collect { (enrollType, sensorType) ->
+            textModel =
+              when (enrollType) {
+                Unicorn -> getUnicornTextModel()
+                else -> getNormalTextModel()
+              }
+
+            setupFooterBarAndScrollView(view)
+
+            val layout = view as GlifLayout
+
+            layout.setHeaderText(textModel.headerText)
+            layout.setDescriptionText(textModel.descriptionText)
 
             // Set color filter for the following icons.
             val colorFilter = getIconColorFilter()
@@ -158,9 +182,9 @@
             view.requireViewById<TextView?>(R.id.footer_title_1).setText(textModel.footerTitleOne)
             view.requireViewById<TextView?>(R.id.footer_title_2).setText(textModel.footerTitleOne)
           }
-        }
+      }
+      return view
     }
-  }
 
   private fun setFooterLink(view: View) {
     val footerLink: TextView = view.requireViewById(R.id.footer_learn_more)
@@ -185,17 +209,18 @@
         navigationViewModel.nextStep()
       }
 
-    val layout: GlifLayout = requireActivity().requireViewById(R.id.setup_wizard_layout)
+    val layout: GlifLayout = view.findViewById(R.id.setup_wizard_layout)!!
     footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
     footerBarMixin.primaryButton =
-      FooterButton.Builder(requireActivity())
+      FooterButton.Builder(requireContext())
         .setText(R.string.security_settings_face_enroll_introduction_more)
         .setListener(onNextButtonClick)
         .setButtonType(FooterButton.ButtonType.OPT_IN)
         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
         .build()
+
     footerBarMixin.setSecondaryButton(
-      FooterButton.Builder(requireActivity())
+      FooterButton.Builder(requireContext())
         .setText(textModel.negativeButton)
         .setListener({ Log.d(TAG, "prevClicked") })
         .setButtonType(FooterButton.ButtonType.NEXT)
@@ -211,8 +236,8 @@
 
     val requireScrollMixin = layout.getMixin(RequireScrollMixin::class.java)
     requireScrollMixin.requireScrollWithButton(
-      requireActivity(),
-      footerBarMixin.primaryButton,
+      requireContext(),
+      primaryButton,
       R.string.security_settings_face_enroll_introduction_more,
       onNextButtonClick
     )
@@ -224,7 +249,7 @@
       }
     }
 
-    lifecycleScope.launch {
+    viewLifecycleOwner.lifecycleScope.launch {
       fingerprintScrollViewModel.hasReadConsentScreen.collect { consented ->
         if (consented) {
           primaryButton.setText(
@@ -244,7 +269,7 @@
     // the flow. For instance if someone launches the activity with an invalid challenge, it
     // either 1) Fails or 2) Launched confirmDeviceCredential
     primaryButton.isEnabled = false
-    lifecycleScope.launch {
+    viewLifecycleOwner.lifecycleScope.launch {
       gateKeeperViewModel.hasValidGatekeeperInfo.collect { primaryButton.isEnabled = it }
     }
   }
@@ -284,8 +309,4 @@
       PorterDuff.Mode.SRC_IN
     )
   }
-
-  private fun getLayout(): GlifLayout? {
-    return requireView().findViewById(R.id.setup_wizard_layout) as GlifLayout?
-  }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
index d2bb321..97c8271 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
@@ -31,11 +31,6 @@
 
 private const val TAG = "FingerprintEnrollNavigationViewModel"
 
-/** Interface to validate a gatekeeper hat */
-interface Validator {
-  fun validateGateKeeper(challenge: Long?): Boolean
-}
-
 /**
  * The [EnrollType] for fingerprint enrollment indicates information on how the flow should behave.
  */
@@ -56,7 +51,6 @@
  */
 class FingerprintEnrollNavigationViewModel(
   private val dispatcher: CoroutineDispatcher,
-  private val validator: Validator,
   private val fingerprintManagerInteractor: FingerprintManagerInteractor,
   private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
   private val canSkipConfirm: Boolean
@@ -145,11 +139,6 @@
 
       return FingerprintEnrollNavigationViewModel(
         backgroundDispatcher,
-        object : Validator {
-          override fun validateGateKeeper(challenge: Long?): Boolean {
-            return challenge != null
-          }
-        },
         fingerprintManagerInteractor,
         fingerprintGatekeeperViewModel,
         canSkipConfirm,
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index fbfd888..1fd4f25 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -25,6 +25,8 @@
         "androidx.fragment_fragment-testing",
         "frameworks-base-testutils",
         "androidx.fragment_fragment",
+        "androidx.lifecycle_lifecycle-runtime-testing",
+        "kotlinx_coroutines_test",
     ],
 
     aaptflags: ["--extra-packages com.android.settings"],
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt
new file mode 100644
index 0000000..cea6676
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint2.fragment
+
+import android.content.Context
+import android.os.Bundle
+import androidx.fragment.app.testing.FragmentScenario
+import androidx.fragment.app.testing.launchFragmentInContainer
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.Visibility
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.runner.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
+import com.android.settings.testutils2.FakeFingerprintManagerInteractor
+import com.google.android.setupdesign.GlifLayout
+import com.google.android.setupdesign.template.RequireScrollMixin
+import kotlinx.coroutines.test.StandardTestDispatcher
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class FingerprintEnrollIntroFragmentTest {
+  private var context: Context = ApplicationProvider.getApplicationContext()
+  private var interactor = FakeFingerprintManagerInteractor()
+
+  private val gatekeeperViewModel =
+    FingerprintGatekeeperViewModel(
+      GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
+      interactor
+    )
+  private val backgroundDispatcher = StandardTestDispatcher()
+  private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
+
+  private val navigationViewModel =
+    FingerprintEnrollNavigationViewModel(
+      backgroundDispatcher,
+      interactor,
+      gatekeeperViewModel,
+      canSkipConfirm = true,
+    )
+  private var fingerprintViewModel = FingerprintEnrollViewModel(interactor, backgroundDispatcher)
+  private var fingerprintScrollViewModel = FingerprintScrollViewModel()
+
+  @Before
+  fun setup() {
+    val factory =
+      object : ViewModelProvider.Factory {
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(
+          modelClass: Class<T>,
+        ): T {
+          return when (modelClass) {
+            FingerprintEnrollViewModel::class.java -> fingerprintViewModel
+            FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
+            FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel
+            FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
+            else -> null
+          }
+            as T
+        }
+      }
+
+    fragmentScenario =
+      launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) {
+        FingerprintEnrollIntroV2Fragment(factory)
+      }
+  }
+
+  @Test
+  fun testScrollToBottomButtonChangesText() {
+    fragmentScenario.onFragment { fragment ->
+      onView(withText("I agree")).check(doesNotExist())
+      val someView = (fragment.requireView().findViewById<GlifLayout>(R.id.setup_wizard_layout))!!
+      val scrollMixin = someView.getMixin(RequireScrollMixin::class.java)!!
+      val listener = scrollMixin.onRequireScrollStateChangedListener
+      // This actually changes the button text
+      listener.onRequireScrollStateChanged(false)
+
+      onView(withText("I agree")).check(matches(isDisplayed()))
+    }
+  }
+
+  @Test
+  fun testBasicTitle() {
+    onView(withText(R.string.security_settings_fingerprint_enroll_introduction_title))
+      .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+  }
+
+  @Test
+  fun testFooterMessageTwo() {
+    onView(withId(R.id.footer_message_2))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageThree() {
+    onView(withId(R.id.footer_message_3))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageFour() {
+    onView(withId(R.id.footer_message_4))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageFive() {
+    onView(withId(R.id.footer_message_5))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5)
+            )
+          )
+        )
+      )
+  }
+}