Added UI tests for FingerprintEnrollIntro

Test: m -j40 RunSettingsRoboTests ROBOTEST_FILTER=FingerprintEnrollmentIntroFragmentTest
Bug: 295206367
Change-Id: I70f6b50dd2604e01805df04ffb1c07a9134ba065
This commit is contained in:
Joshua McCloskey
2023-08-28 20:36:09 +00:00
parent 9952d054d9
commit b7021c8e0b
6 changed files with 414 additions and 215 deletions

View File

@@ -42,6 +42,7 @@ import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
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 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
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 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
)[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 =

View File

@@ -25,10 +25,13 @@ import android.os.Bundle
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 @@ private data class TextModel(
* 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 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
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 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
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 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
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 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
}
}
lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launch {
fingerprintScrollViewModel.hasReadConsentScreen.collect { consented ->
if (consented) {
primaryButton.setText(
@@ -244,7 +269,7 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
// 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 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
PorterDuff.Mode.SRC_IN
)
}
private fun getLayout(): GlifLayout? {
return requireView().findViewById(R.id.setup_wizard_layout) as GlifLayout?
}
}

View File

@@ -31,11 +31,6 @@ import kotlinx.coroutines.launch
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 @@ object Unicorn : EnrollType()
*/
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 @@ class FingerprintEnrollNavigationViewModel(
return FingerprintEnrollNavigationViewModel(
backgroundDispatcher,
object : Validator {
override fun validateGateKeeper(challenge: Long?): Boolean {
return challenge != null
}
},
fingerprintManagerInteractor,
fingerprintGatekeeperViewModel,
canSkipConfirm,