From 199d75b332fd15425a6bc742ebaa4c610f0d2713 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Fri, 16 Feb 2024 21:17:36 +0000 Subject: [PATCH] UDFPS Enrollment Refactor (1/N) This CL creates a few necessary components that are needed to create the UI Test: adb shell device_config put biometrics_framework com.android.settings.flags.fingerprint_v2_enrollment true Bug: 297082837 Change-Id: I17c4f65fdeac4ebf3c19ba69f5928787b5ace52e --- .../fingerprint_v2_udfps_enroll_enrolling.xml | 70 ++++++++++ .../FingerprintEnrollmentV2Activity.kt | 11 ++ .../udfps/ui/fragment/UdfpsEnrollFragment.kt | 127 ++++++++++++++++++ .../udfps/ui/viewmodel/StageViewModel.kt | 36 +++++ .../udfps/ui/viewmodel/UdfpsViewModel.kt | 42 ++++++ 5 files changed, 286 insertions(+) create mode 100644 res/layout/fingerprint_v2_udfps_enroll_enrolling.xml create mode 100644 src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt create mode 100644 src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt create mode 100644 src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt diff --git a/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml new file mode 100644 index 00000000000..32df66592f3 --- /dev/null +++ b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + 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 d26b812a686..70d58eab776 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 @@ -52,6 +52,8 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel 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 @@ -100,6 +102,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel private lateinit var fingerprintEnrollConfirmationViewModel: FingerprintEnrollConfirmationViewModel + private lateinit var udfpsViewModel: UdfpsViewModel private val coroutineDispatcher = Dispatchers.Default /** Result listener for ChooseLock activity flow. */ @@ -306,6 +309,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { ), )[RFPSViewModel::class.java] + udfpsViewModel = + ViewModelProvider( + this, + UdfpsViewModel.UdfpsEnrollmentFactory(), + )[UdfpsViewModel::class.java] + fingerprintEnrollConfirmationViewModel = ViewModelProvider( this, @@ -344,6 +353,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { is Enrollment -> { when (step.sensor.sensorType) { FingerprintSensorType.REAR -> RFPSEnrollFragment() + FingerprintSensorType.UDFPS_OPTICAL, + FingerprintSensorType.UDFPS_ULTRASONIC -> UdfpsEnrollFragment() else -> FingerprintEnrollEnrollingV2Fragment() } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt new file mode 100644 index 00000000000..61451287dc6 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt @@ -0,0 +1,127 @@ +/* + * 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.modules.enrolling.udfps.ui.fragment + +import android.os.Bundle +import android.util.Log +import android.view.View +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.airbnb.lottie.LottieAnimationView +import com.airbnb.lottie.LottieCompositionFactory +import com.android.settings.R +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel +import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep +import com.google.android.setupdesign.GlifLayout +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enrolling) { + + /** Used for testing purposes */ + private var factory: ViewModelProvider.Factory? = null + private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] } + + @VisibleForTesting + constructor(theFactory: ViewModelProvider.Factory) : this() { + factory = theFactory + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val layout = view as GlifLayout + val illustrationLottie: LottieAnimationView = layout.findViewById(R.id.illustration_lottie)!! + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewLifecycleOwner.lifecycleScope.launch { + viewModel.stageFlow.collect { + layout.setHeaderText(getHeaderText(it)) + getDescriptionText(it)?.let { descriptionText -> + layout.setDescriptionText(descriptionText) + } + getLottie(it)?.let { lottie -> + layout.descriptionText = "" + LottieCompositionFactory.fromRawRes(requireContext().applicationContext, lottie) + .addListener { comp -> + comp?.let { composition -> + viewLifecycleOwner.lifecycleScope.launch { + illustrationLottie.setComposition(composition) + illustrationLottie.visibility = View.VISIBLE + illustrationLottie.playAnimation() + } + } + } + } + } + } + } + } + } + + private fun getHeaderText(stageViewModel: StageViewModel): Int { + return when (stageViewModel) { + StageViewModel.Center, + StageViewModel.Guided, + StageViewModel.Fingertip, + StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title + StageViewModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title + StageViewModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title + } + } + + private fun getDescriptionText(stageViewModel: StageViewModel): Int? { + return when (stageViewModel) { + StageViewModel.Center, + StageViewModel.Guided, + StageViewModel.Fingertip, + StageViewModel.LeftEdge, + StageViewModel.RightEdge -> null + StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_start_message + } + } + + private fun getLottie(stageViewModel: StageViewModel): Int? { + return when (stageViewModel) { + StageViewModel.Center, + StageViewModel.Guided -> R.raw.udfps_center_hint_lottie + StageViewModel.Fingertip -> R.raw.udfps_tip_hint_lottie + StageViewModel.LeftEdge -> R.raw.udfps_left_edge_hint_lottie + StageViewModel.RightEdge -> R.raw.udfps_right_edge_hint_lottie + StageViewModel.Unknown -> null + } + } + + private val viewModelProvider: ViewModelProvider by lazy { + if (factory != null) { + ViewModelProvider(requireActivity(), factory!!) + } else { + ViewModelProvider(requireActivity()) + } + } + + companion object { + private const val TAG = "UDFPSEnrollFragment" + private val navStep = FingerprintNavigationStep.Enrollment::class + } +} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt new file mode 100644 index 00000000000..b879ce17e7b --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt @@ -0,0 +1,36 @@ +/* + * 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.modules.enrolling.udfps.ui.viewmodel + +/** + * A view model that describes the various stages of UDFPS Enrollment. This stages typically update + * the enrollment UI in a major way, such as changing the lottie animation or changing the location + * of the where the user should press their fingerprint + */ +sealed class StageViewModel { + data object Unknown : StageViewModel() + + data object Guided : StageViewModel() + + data object Center : StageViewModel() + + data object Fingertip : StageViewModel() + + data object LeftEdge : StageViewModel() + + data object RightEdge : StageViewModel() +} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt new file mode 100644 index 00000000000..4fc3d1c5446 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt @@ -0,0 +1,42 @@ +/* + * 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.modules.enrolling.udfps.ui.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep +import kotlinx.coroutines.flow.flowOf + +/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */ +class UdfpsViewModel() : ViewModel() { + + /** Indicates what stage UDFPS enrollment is in. */ + val stageFlow = flowOf(StageViewModel.Center) + + class UdfpsEnrollmentFactory() : ViewModelProvider.Factory { + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return UdfpsViewModel() as T + } + } + + companion object { + private val navStep = FingerprintNavigationStep.Enrollment::class + private const val TAG = "UDFPSViewModel" + } +}