diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/model/StageViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/data/model/EnrollStageModel.kt similarity index 80% rename from src/com/android/settings/biometrics/fingerprint2/lib/model/StageViewModel.kt rename to src/com/android/settings/biometrics/fingerprint2/data/model/EnrollStageModel.kt index 81bba157291..ab6ef217b0d 100644 --- a/src/com/android/settings/biometrics/fingerprint2/lib/model/StageViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/data/model/EnrollStageModel.kt @@ -14,38 +14,38 @@ * limitations under the License. */ -package com.android.settings.biometrics.fingerprint2.lib.model +package com.android.settings.biometrics.fingerprint2.data.model /** * 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 { +sealed class EnrollStageModel { /** Unknown stage */ - data object Unknown : StageViewModel() + data object Unknown : EnrollStageModel() /** This is the stage that moves the fingerprint icon around during enrollment. */ - data object Guided : StageViewModel() + data object Guided : EnrollStageModel() /** The center stage is the initial stage of enrollment. */ - data object Center : StageViewModel() + data object Center : EnrollStageModel() /** * Fingerprint stage of enrollment. Typically there is some sort of indication that a user should * be using their finger tip to enroll. */ - data object Fingertip : StageViewModel() + data object Fingertip : EnrollStageModel() /** * Left edge stage of enrollment. Typically there is an indication that a user should be using the * left edge of their fingerprint. */ - data object LeftEdge : StageViewModel() + data object LeftEdge : EnrollStageModel() /** * Right edge stage of enrollment. Typically there is an indication that a user should be using * the right edge of their fingerprint. */ - data object RightEdge : StageViewModel() + data object RightEdge : EnrollStageModel() } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt index 2d4cb409868..e683cb84cf9 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/EnrollStageInteractor.kt @@ -16,11 +16,11 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel +import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -typealias EnrollStageThresholds = Map +typealias EnrollStageThresholds = Map /** Interactor that provides enroll stages for enrollment. */ interface EnrollStageInteractor { @@ -33,11 +33,11 @@ class EnrollStageInteractorImpl() : EnrollStageInteractor { override val enrollStageThresholds: Flow = flowOf( mapOf( - 0.0f to StageViewModel.Center, - 0.25f to StageViewModel.Guided, - 0.5f to StageViewModel.Fingertip, - 0.75f to StageViewModel.LeftEdge, - 0.875f to StageViewModel.RightEdge, + 0.0f to EnrollStageModel.Center, + 0.25f to EnrollStageModel.Guided, + 0.5f to EnrollStageModel.Fingertip, + 0.75f to EnrollStageModel.LeftEdge, + 0.875f to EnrollStageModel.RightEdge, ) ) } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt index 5d1d8c86e37..f9276e63ddf 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt @@ -42,6 +42,7 @@ interface OrientationInteractor { * A flow that contains the rotation info matched against the def [config_reverseDefaultRotation] */ val rotationFromDefault: Flow + /** * A Helper function that computes rotation if device is in * [R.bool.config_reverseDefaultConfigRotation] diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt new file mode 100644 index 00000000000..ec09ffd0011 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt @@ -0,0 +1,96 @@ +/* + * 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.domain.interactor + +import android.graphics.PointF +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.update + +/** + * This interactor provides information about the current offset of the sensor for guided enrollment + * on UDFPS devices. + */ +interface UdfpsEnrollInteractor { + /** Indicates at which step a UDFPS enrollment is in. */ + fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) + + /** Indicates if guided enrollment should be enabled or not. */ + fun updateGuidedEnrollment(enabled: Boolean) + + /** + * A flow indicating how much the sensor image drawable should be offset for guided enrollment. A + * null point indicates that the icon should be in its default position. + */ + val guidedEnrollmentOffset: Flow +} + +/** Keeps track of which guided enrollment point we should be using */ +class UdfpsEnrollInteractorImpl( + pixelsPerMillimeter: Float, + accessibilityInteractor: AccessibilityInteractor, +) : UdfpsEnrollInteractor { + + private var isGuidedEnrollment = MutableStateFlow(false) + // Number of pixels per mm + val px = pixelsPerMillimeter + private val guidedEnrollmentPoints: MutableList = + mutableListOf( + PointF(2.00f * px, 0.00f * px), + PointF(0.87f * px, -2.70f * px), + PointF(-1.80f * px, -1.31f * px), + PointF(-1.80f * px, 1.31f * px), + PointF(0.88f * px, 2.70f * px), + PointF(3.94f * px, -1.06f * px), + PointF(2.90f * px, -4.14f * px), + PointF(-0.52f * px, -5.95f * px), + PointF(-3.33f * px, -3.33f * px), + PointF(-3.99f * px, -0.35f * px), + PointF(-3.62f * px, 2.54f * px), + PointF(-1.49f * px, 5.57f * px), + PointF(2.29f * px, 4.92f * px), + PointF(3.82f * px, 1.78f * px), + ) + + override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) { + val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size + _guidedEnrollment.update { guidedEnrollmentPoints[index] } + } + + override fun updateGuidedEnrollment(enabled: Boolean) { + isGuidedEnrollment.update { enabled } + } + + private val _guidedEnrollment = MutableStateFlow(PointF(0f, 0f)) + override val guidedEnrollmentOffset: Flow = + combine( + _guidedEnrollment, + accessibilityInteractor.isAccessibilityEnabled, + isGuidedEnrollment, + ) { point, accessibilityEnabled, guidedEnrollmentEnabled -> + if (accessibilityEnabled || !guidedEnrollmentEnabled) { + return@combine PointF(0f, 0f) + } else { + return@combine PointF(point.x * SCALE, point.y * SCALE) + } + } + + companion object { + private const val SCALE = 0.5f + } +} 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 c8e9ca38d85..6d353a42a62 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 @@ -24,6 +24,7 @@ import android.hardware.fingerprint.FingerprintManager import android.os.Bundle import android.os.Vibrator import android.util.Log +import android.util.TypedValue import android.view.accessibility.AccessibilityManager import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment @@ -54,6 +55,8 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateI 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.UdfpsEnrollInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl import com.android.settings.biometrics.fingerprint2.lib.model.Default @@ -89,6 +92,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing 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.biometrics.fingerprint2.ui.enrollment.viewmodel.Transition import com.android.settings.flags.Flags import com.android.settings.password.ChooseLockGeneric import com.android.settings.password.ChooseLockSettingsHelper @@ -116,6 +120,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private lateinit var foldStateInteractor: FoldStateInteractor private lateinit var orientationInteractor: OrientationInteractor private lateinit var displayDensityInteractor: DisplayDensityInteractor + private lateinit var udfpsEnrollInteractor: UdfpsEnrollInteractor private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel private lateinit var backgroundViewModel: BackgroundViewModel private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel @@ -256,6 +261,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { fingerprintManager, Settings, ) + val accessibilityInteractor = + AccessibilityInteractorImpl( + getSystemService(AccessibilityManager::class.java)!!, + lifecycleScope, + ) + + val pixelsPerMillimeter = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, context.resources.displayMetrics) + udfpsEnrollInteractor = UdfpsEnrollInteractorImpl(pixelsPerMillimeter, accessibilityInteractor) val fingerprintManagerInteractor = FingerprintManagerInteractorImpl( @@ -273,12 +287,6 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { val hasConfirmedDeviceCredential = gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo - val accessibilityInteractor = - AccessibilityInteractorImpl( - getSystemService(AccessibilityManager::class.java)!!, - lifecycleScope, - ) - navigationViewModel = ViewModelProvider( this, @@ -384,6 +392,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { orientationInteractor, backgroundViewModel, fingerprintSensorRepo, + udfpsEnrollInteractor, ), )[UdfpsViewModel::class.java] @@ -435,17 +444,17 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { else -> FingerprintEnrollEnrollingV2Fragment() } } - Introduction -> FingerprintEnrollIntroV2Fragment() + is Introduction -> FingerprintEnrollIntroV2Fragment() else -> null } if (theClass != null) { - supportFragmentManager.fragments.onEach { fragment -> - supportFragmentManager.beginTransaction().remove(fragment).commit() - } - supportFragmentManager .beginTransaction() + .setCustomAnimations( + step.enterTransition.toAnimation(), + step.exitTransition.toAnimation(), + ) .setReorderingAllowed(true) .add(R.id.fragment_container_view, theClass::class.java, null) .commit() @@ -512,3 +521,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { } } } + +private fun Transition.toAnimation(): Int { + return when (this) { + Transition.EnterFromLeft -> com.google.android.setupdesign.R.anim.sud_slide_back_in + Transition.EnterFromRight -> com.google.android.setupdesign.R.anim.sud_slide_next_in + Transition.ExitToLeft -> com.google.android.setupdesign.R.anim.sud_slide_next_out + Transition.ExitToRight -> com.google.android.setupdesign.R.anim.sud_slide_back_out + } +} 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 index c96a1b45302..a2e52329dd8 100644 --- 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 @@ -32,12 +32,12 @@ 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.data.model.EnrollStageModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.HeaderText import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep @@ -83,6 +83,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro window.statusBarColor = color view.setBackgroundColor(color) + udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() } + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { launch { @@ -159,7 +161,14 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro } viewLifecycleOwner.lifecycleScope.launch { - viewModel.enrollStage.collect { udfpsEnrollView.updateStage(it) } + viewModel.guidedEnrollment.collect { + glifLayout.post { udfpsEnrollView.updateGuidedEnrollment(it) } + } + } + viewLifecycleOwner.lifecycleScope.launch { + viewModel.guidedEnrollmentSaved.collect { + glifLayout.post { udfpsEnrollView.onGuidedPointSaved(it) } + } } } } @@ -175,35 +184,35 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro } private fun HeaderText.toResource(): Int { - return when (this.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 + return when (this.enrollStageModel) { + EnrollStageModel.Center, + EnrollStageModel.Guided, + EnrollStageModel.Fingertip, + EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title + EnrollStageModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title + EnrollStageModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title } } private fun DescriptionText.toResource(): Int? { - return when (this.stageViewModel) { - StageViewModel.Center, - StageViewModel.Guided, - StageViewModel.Fingertip, - StageViewModel.LeftEdge, - StageViewModel.RightEdge -> null - StageViewModel.Unknown -> R.string.security_settings_udfps_enroll_start_message + return when (this.enrollStageModel) { + EnrollStageModel.Center, + EnrollStageModel.Guided, + EnrollStageModel.Fingertip, + EnrollStageModel.LeftEdge, + EnrollStageModel.RightEdge -> null + EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_start_message } } private fun EducationAnimationModel.toResource(): Int? { - return when (this.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 + return when (this.enrollStageModel) { + EnrollStageModel.Center, + EnrollStageModel.Guided -> R.raw.udfps_center_hint_lottie + EnrollStageModel.Fingertip -> R.raw.udfps_tip_hint_lottie + EnrollStageModel.LeftEdge -> R.raw.udfps_left_edge_hint_lottie + EnrollStageModel.RightEdge -> R.raw.udfps_right_edge_hint_lottie + EnrollStageModel.Unknown -> null } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/DescriptionText.kt similarity index 84% rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt rename to src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/DescriptionText.kt index 175fea0ac13..a949545ec19 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/DescriptionText.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel +package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel +import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel /** Represents the description text for UDFPS enrollment */ data class DescriptionText( val isSuw: Boolean, val isAccessibility: Boolean, - val stageViewModel: StageViewModel, + val enrollStageModel: EnrollStageModel, ) diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/HeaderText.kt similarity index 83% rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt rename to src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/HeaderText.kt index c565f35487c..9e45107537f 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/model/HeaderText.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel +package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel +import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel /** Represents the header text for UDFPS enrollment */ data class HeaderText( val isSuw: Boolean, val isAccessibility: Boolean, - val stageViewModel: StageViewModel, + val enrollStageModel: EnrollStageModel, ) diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt index a274179e303..64f345f13a5 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt @@ -16,11 +16,11 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel +import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel /** Represents the lottie for UDFPS enrollment */ data class EducationAnimationModel( val isSuw: Boolean, val isAccessibility: Boolean, - val stageViewModel: StageViewModel, + val enrollStageModel: EnrollStageModel, ) 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 index 37822378f21..a22f680b641 100644 --- 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 @@ -17,20 +17,24 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel import android.graphics.Point +import android.graphics.PointF import android.view.Surface import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository +import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText 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.FingerprintEnrollEnrollingViewModel @@ -61,9 +65,11 @@ class UdfpsViewModel( orientationInteractor: OrientationInteractor, backgroundViewModel: BackgroundViewModel, sensorRepository: FingerprintSensorRepository, + udfpsEnrollInteractor: UdfpsEnrollInteractor, ) : ViewModel() { private val isSetupWizard = flowOf(false) + private var shouldResetErollment = false private var _enrollState: Flow = fingerprintEnrollEnrollingViewModel.enrollFlow @@ -112,6 +118,17 @@ class UdfpsViewModel( } } + /** + * This indicates at which point the UI should offset the fingerprint sensor icon for guided + * enrollment. + */ + val guidedEnrollment: Flow = + udfpsEnrollInteractor.guidedEnrollmentOffset.distinctUntilChanged() + + /** The saved version of [guidedEnrollment] */ + val guidedEnrollmentSaved: Flow = + guidedEnrollment.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1) + /** * This is the saved progress, this is for when views are recreated and need saved state for the * first time. @@ -132,13 +149,13 @@ class UdfpsViewModel( } } - /** Determines the current [StageViewModel] enrollment is in */ - val enrollStage: Flow = + /** Determines the current [EnrollStageModel] enrollment is in */ + private val enrollStage: Flow = combine(enrollStageInteractor.enrollStageThresholds, enrollState) { thresholds, event -> if (event is FingerEnrollState.EnrollProgress) { val progress = (event.totalStepsRequired - event.remainingSteps).toFloat() / event.totalStepsRequired - var stageToReturn: StageViewModel = StageViewModel.Center + var stageToReturn: EnrollStageModel = EnrollStageModel.Center thresholds.forEach { (threshold, stage) -> if (progress < threshold) { return@forEach @@ -153,6 +170,40 @@ class UdfpsViewModel( .filterNotNull() .shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1) + init { + viewModelScope.launch { + enrollState + .combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) } + .collect { + if ( + when (it.first) { + is FingerEnrollState.EnrollError -> true + is FingerEnrollState.EnrollHelp -> it.second + is FingerEnrollState.EnrollProgress -> true + else -> false + } + ) { + vibrate(it.first) + } + } + } + viewModelScope.launch { + enrollStage.collect { + udfpsEnrollInteractor.updateGuidedEnrollment(it is EnrollStageModel.Guided) + } + } + + viewModelScope.launch { + enrollState.filterIsInstance().collect { + udfpsEnrollInteractor.onEnrollmentStep(it.remainingSteps, it.totalStepsRequired) + } + } + + viewModelScope.launch { + backgroundViewModel.background.filter { true }.collect { didGoToBackground() } + } + } + /** Indicates if we should show the lottie. */ val shouldShowLottie: Flow = combine( @@ -183,7 +234,7 @@ class UdfpsViewModel( } .shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1) - private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown } + private val shouldClearDescriptionText = enrollStage.map { it is EnrollStageModel.Unknown } /** The description text for UDFPS enrollment */ val descriptionText: Flow = @@ -202,6 +253,10 @@ class UdfpsViewModel( /** Indicates if the consumer is ready for enrollment */ fun readyForEnrollment() { + if (shouldResetErollment) { + shouldResetErollment = false + _enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow + } fingerprintEnrollEnrollingViewModel.canEnroll() } @@ -237,8 +292,12 @@ class UdfpsViewModel( } private fun doReset() { - /** Indicates if the icon should be animating or not */ _enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow + progressSaved = + enrollState + .filterIsInstance() + .filterNotNull() + .shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1) } /** The lottie that should be shown for UDFPS Enrollment */ @@ -272,6 +331,7 @@ class UdfpsViewModel( private val orientationInteractor: OrientationInteractor, private val backgroundViewModel: BackgroundViewModel, private val sensorRepository: FingerprintSensorRepository, + private val udfpsEnrollInteractor: UdfpsEnrollInteractor, ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -287,6 +347,7 @@ class UdfpsViewModel( orientationInteractor, backgroundViewModel, sensorRepository, + udfpsEnrollInteractor, ) as T } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt deleted file mode 100644 index 141924161d1..00000000000 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.widget - -import android.content.Context -import android.graphics.PointF -import android.util.TypedValue -import android.view.accessibility.AccessibilityManager -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel - -/** Keeps track of which guided enrollment point we should be using */ -class UdfpsEnrollHelperV2(private val mContext: Context) { - - private var isGuidedEnrollment: Boolean = false - private val accessibilityEnabled: Boolean - private val guidedEnrollmentPoints: MutableList - /** The current index of [guidedEnrollmentPoints] for the guided enrollment. */ - private var index = 0 - - init { - val am = mContext.getSystemService(AccessibilityManager::class.java) - accessibilityEnabled = am!!.isEnabled - guidedEnrollmentPoints = ArrayList() - - // Number of pixels per mm - val px = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, mContext.resources.displayMetrics) - guidedEnrollmentPoints.add(PointF(2.00f * px, 0.00f * px)) - guidedEnrollmentPoints.add(PointF(0.87f * px, -2.70f * px)) - guidedEnrollmentPoints.add(PointF(-1.80f * px, -1.31f * px)) - guidedEnrollmentPoints.add(PointF(-1.80f * px, 1.31f * px)) - guidedEnrollmentPoints.add(PointF(0.88f * px, 2.70f * px)) - guidedEnrollmentPoints.add(PointF(3.94f * px, -1.06f * px)) - guidedEnrollmentPoints.add(PointF(2.90f * px, -4.14f * px)) - guidedEnrollmentPoints.add(PointF(-0.52f * px, -5.95f * px)) - guidedEnrollmentPoints.add(PointF(-3.33f * px, -3.33f * px)) - guidedEnrollmentPoints.add(PointF(-3.99f * px, -0.35f * px)) - guidedEnrollmentPoints.add(PointF(-3.62f * px, 2.54f * px)) - guidedEnrollmentPoints.add(PointF(-1.49f * px, 5.57f * px)) - guidedEnrollmentPoints.add(PointF(2.29f * px, 4.92f * px)) - guidedEnrollmentPoints.add(PointF(3.82f * px, 1.78f * px)) - } - - /** - * This indicates whether we should be offsetting the enrollment icon based on - * [guidedEnrollmentPoints] - */ - fun onUpdateStage(stage: StageViewModel) { - this.isGuidedEnrollment = stage is StageViewModel.Guided - } - - /** Updates [index] to be used by [guidedEnrollmentPoints] */ - fun onEnrollmentProgress(remaining: Int, totalSteps: Int) { - index = totalSteps - remaining - } - - /** - * Returns the current guided enrollment point, or (0,0) if we are not in guided enrollment or are - * in accessibility. - */ - val guidedEnrollmentLocation: PointF? - get() { - if (accessibilityEnabled || !isGuidedEnrollment) { - return null - } - val scale = SCALE - val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size] - return PointF(originalPoint.x * scale, originalPoint.y * scale) - } - - companion object { - private const val TAG = "UdfpsEnrollHelperV2" - private const val SCALE = 0.5f - } -} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt index 0d489954b80..c209c55cb76 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt @@ -24,6 +24,7 @@ import android.graphics.Canvas import android.graphics.ColorFilter import android.graphics.Paint import android.graphics.PixelFormat +import android.graphics.PointF import android.graphics.Rect import android.graphics.RectF import android.graphics.drawable.Drawable @@ -37,7 +38,6 @@ import androidx.core.animation.addListener import androidx.core.graphics.toRect import androidx.core.graphics.toRectF import com.android.settings.R -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel import kotlin.math.sin /** @@ -51,11 +51,11 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS private val fingerprintDrawable: ShapeDrawable private val sensorOutlinePaint: Paint private val blueFill: Paint - private val helper = UdfpsEnrollHelperV2(context) @ColorInt private var enrollIconColor = 0 @ColorInt private var movingTargetFill = 0 private var currentScale = 1.0f private var alpha = 0 + private var guidedEnrollmentOffset: PointF? = null /** * This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt] @@ -143,45 +143,6 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS invalidateSelf() } - /** Update the progress of the icon */ - fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) { - restoreAnimationTime() - // If we are restoring this view from a saved state, set animation duration to 0 to avoid - // animating progress that has already occurred. - if (isRecreating) { - setAnimationTimeToZero() - } else { - restoreAnimationTime() - } - - helper.onEnrollmentProgress(remaining, totalSteps) - val offset = helper.guidedEnrollmentLocation - val currentBounds = getCurrLocation().toRect() - if (offset != null) { - // This is the desired location of the sensor rect, the [EnrollHelper] - // offsets the initial sensor rect by a bit to get the user to move their finger a bit more. - val targetRect = Rect(sensorRectBounds).toRectF() - targetRect.offset(offset.x, offset.y) - val shouldAnimateMovement = - !currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f - if (shouldAnimateMovement) { - targetAnimatorSet?.cancel() - animateMovement(currentBounds, targetRect, true) - } - } else { - // If we are not offsetting the sensor, move it back to its original place - animateMovement(currentBounds, sensorRectBounds.toRectF(), false) - } - - invalidateSelf() - } - - /** Update the stage of the icon */ - fun updateStage(it: StageViewModel) { - helper.onUpdateStage(it) - invalidateSelf() - } - /** Stop drawing the fingerprint icon. */ fun stopDrawing() { alpha = 0 @@ -211,6 +172,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS if (currentBounds.equals(offsetRect)) { return } + val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left) xAnimator.addUpdateListener { currX = it.animatedValue as Float @@ -260,6 +222,40 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS targetAnimationDuration = TARGET_ANIM_DURATION_LONG } + /** + * Indicates a change to guided enrollment has occurred. Also indicates if we are recreating the + * view, in which case their is no need to animate the icon to whatever position it was in. + */ + fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) { + guidedEnrollmentOffset = point + if (isRecreating) { + setAnimationTimeToZero() + } else { + restoreAnimationTime() + } + + val currentBounds = getCurrLocation().toRect() + val offset = guidedEnrollmentOffset + if (offset?.x != 0f && offset?.y != 0f) { + val targetRect = Rect(sensorRectBounds).toRectF() + // This is the desired location of the sensor rect, the [EnrollHelper] + // offsets the initial sensor rect by a bit to get the user to move their finger a bit more. + targetRect.offset(offset!!.x, offset!!.y) + val shouldAnimateMovement = !currentBounds.equals(targetRect) + if (shouldAnimateMovement) { + targetAnimatorSet?.cancel() + animateMovement(currentBounds, targetRect, true) + } else { + // If we are not offsetting the sensor, move it back to its original place + animateMovement(currentBounds, sensorRectBounds.toRectF(), false) + } + } else { + // If we are not offsetting the sensor, move it back to its original place + animateMovement(currentBounds, sensorRectBounds.toRectF(), false) + } + invalidateSelf() + } + companion object { private const val TAG = "UdfpsEnrollDrawableV2" private const val DEFAULT_STROKE_WIDTH = 3f diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt index 8f0e84564a6..bf2f0261112 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt @@ -27,10 +27,12 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.util.AttributeSet import android.util.DisplayMetrics +import android.util.Log import android.view.animation.DecelerateInterpolator import android.view.animation.Interpolator import android.view.animation.OvershootInterpolator import androidx.annotation.ColorInt +import androidx.core.animation.addListener import androidx.core.animation.doOnEnd import androidx.core.graphics.toRectF import com.android.internal.annotations.VisibleForTesting @@ -46,6 +48,7 @@ import kotlin.math.sin class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: AttributeSet?) : Drawable() { private val sensorRect: Rect = Rect() + private var onFinishedCompletionAnimation: (() -> Unit)? = null private var rotation: Int = 0 private val strokeWidthPx: Float @@ -287,6 +290,12 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr checkMarkDrawable.bounds = newBounds checkMarkDrawable.setVisible(true, false) } + doOnEnd { + onFinishedCompletionAnimation?.let{ + it() + } + + } start() } } @@ -380,6 +389,13 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS } + /** + * Indicates that the finish animation has completed, and enrollment can proceed to the next stage + */ + fun setFinishAnimationCompleted(onFinishedAnimation: () -> Unit) { + this.onFinishedCompletionAnimation = onFinishedAnimation + } + companion object { private const val TAG = "UdfpsProgressBar" private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt index b355f7735d7..586408f0c28 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt @@ -18,6 +18,7 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol import android.content.Context import android.graphics.Point +import android.graphics.PointF import android.graphics.Rect import android.util.AttributeSet import android.util.Log @@ -31,7 +32,6 @@ import android.widget.FrameLayout import android.widget.ImageView import com.android.settings.R import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState -import com.android.settings.biometrics.fingerprint2.lib.model.StageViewModel import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams @@ -53,6 +53,13 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co private val udfpsUtils: UdfpsUtils = UdfpsUtils() private lateinit var touchExplorationAnnouncer: TouchExplorationAnnouncer private var isRecreating = false + private var onFinishedCompletionAnimation: (() -> Unit)? = null + + init { + fingerprintProgressDrawable.setFinishAnimationCompleted { + onFinishedCompletionAnimation?.let { it() } + } + } /** * This function computes the center (x,y) location with respect to the parent [FrameLayout] for @@ -112,11 +119,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils) } - /** Updates the current enrollment stage. */ - fun updateStage(it: StageViewModel) { - fingerprintIcon.updateStage(it) - } - /** Receive enroll progress event */ fun onUdfpsEvent(event: FingerEnrollState) { when (event) { @@ -174,7 +176,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co /** Receive enroll progress event */ private fun onEnrollmentProgress(remaining: Int, totalSteps: Int) { - fingerprintIcon.onEnrollmentProgress(remaining, totalSteps) fingerprintProgressDrawable.onEnrollmentProgress(remaining, totalSteps) } @@ -241,10 +242,25 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co /** Indicates we should should restore the views saved state. */ fun onEnrollProgressSaved(it: FingerEnrollState.EnrollProgress) { - fingerprintIcon.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true) fingerprintProgressDrawable.onEnrollmentProgress(it.remainingSteps, it.totalStepsRequired, true) } + /** Indicates we are recreating the UI from a saved state. */ + fun onGuidedPointSaved(it: PointF) { + fingerprintIcon.updateGuidedEnrollment(it, true) + } + + /** + * Indicates that the finish animation has completed, and enrollment can proceed to the next stage + */ + fun setFinishAnimationCompleted(onFinishedAnimation: () -> Unit) { + this.onFinishedCompletionAnimation = onFinishedAnimation + } + + fun updateGuidedEnrollment(point: PointF) { + fingerprintIcon.updateGuidedEnrollment(point, false) + } + companion object { private const val TAG = "UdfpsEnrollView" } 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 76b4895f2bb..ecb330ea62b 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 @@ -88,7 +88,10 @@ sealed interface FingerprintNavigationStep { } /** UiSteps should have a 1 to 1 mapping between each screen of FingerprintEnrollment */ - sealed class UiStep : FingerprintNavigationStep + sealed class UiStep( + val enterTransition: Transition = Transition.EnterFromRight, + val exitTransition: Transition = Transition.ExitToLeft, + ) : FingerprintNavigationStep /** This is the landing page for enrollment, where no content is shown. */ data object Init : UiStep() { @@ -103,7 +106,7 @@ sealed interface FingerprintNavigationStep { } else if (state.flowType is FastEnroll) { TransitionStep(Enrollment(state.fingerprintSensor!!)) } else { - TransitionStep(Introduction) + TransitionStep(Introduction()) } } else -> null @@ -118,7 +121,7 @@ sealed interface FingerprintNavigationStep { action: FingerprintAction, ): FingerprintNavigationStep? { return when (action) { - FingerprintAction.CONFIRM_DEVICE_SUCCESS -> TransitionStep(Introduction) + FingerprintAction.CONFIRM_DEVICE_SUCCESS -> TransitionStep(Introduction()) FingerprintAction.CONFIRM_DEVICE_FAIL -> Finish(null) else -> null } @@ -126,7 +129,10 @@ sealed interface FingerprintNavigationStep { } /** Indicates the FingerprintIntroduction screen is being presented to the user */ - data object Introduction : UiStep() { + class Introduction( + enterTransition: Transition = Transition.EnterFromRight, + exitTransition: Transition = Transition.ExitToLeft, + ) : UiStep(enterTransition, exitTransition) { override fun update( state: NavigationState, action: FingerprintAction, @@ -141,7 +147,11 @@ sealed interface FingerprintNavigationStep { } /** Indicates the FingerprintEducation screen is being presented to the user */ - data class Education(val sensor: FingerprintSensor) : UiStep() { + class Education( + val sensor: FingerprintSensor, + enterTransition: Transition = Transition.EnterFromRight, + exitTransition: Transition = Transition.ExitToLeft, + ) : UiStep(enterTransition, exitTransition) { override fun update( state: NavigationState, action: FingerprintAction, @@ -149,7 +159,8 @@ sealed interface FingerprintNavigationStep { return when (action) { FingerprintAction.NEXT -> TransitionStep(Enrollment(state.fingerprintSensor!!)) FingerprintAction.NEGATIVE_BUTTON_PRESSED, - FingerprintAction.PREV -> TransitionStep(Introduction) + FingerprintAction.PREV -> + TransitionStep(Introduction(Transition.EnterFromLeft, Transition.ExitToRight)) else -> null } } @@ -179,7 +190,10 @@ sealed interface FingerprintNavigationStep { ): FingerprintNavigationStep? { return when (action) { FingerprintAction.NEXT -> Finish(null) - FingerprintAction.PREV -> TransitionStep(Education(state.fingerprintSensor!!)) + FingerprintAction.PREV -> + TransitionStep( + Education(state.fingerprintSensor!!, Transition.EnterFromLeft, Transition.ExitToRight) + ) FingerprintAction.ADD_ANOTHER -> TransitionStep(Enrollment(state.fingerprintSensor!!)) else -> null } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt new file mode 100644 index 00000000000..8fb72916cd7 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt @@ -0,0 +1,41 @@ +/* + * 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 + +/** Indicates the type of transitions that can occur between fragments */ +sealed class Transition { + /** + * Indicates the new fragment should slide in from the left side + */ + data object EnterFromLeft : Transition() + + /** + * Indicates the new fragment should slide in from the right side + */ + data object EnterFromRight : Transition() + + /** + * Indicates the old fragment should slide out to the left side + */ + data object ExitToLeft : Transition() + + /** + * Indicates the old fragment should slide out to the right side + */ + data object ExitToRight : Transition() +} + 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 index e876289866e..e30819bcb2f 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt @@ -90,7 +90,7 @@ class FingerprintEnrollIntroFragmentTest { private val navigationViewModel = FingerprintNavigationViewModel( - Introduction, + Introduction(), false, flowViewModel, interactor diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt index 40a23d7fd56..68d600b1298 100644 --- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt @@ -28,7 +28,7 @@ import platform.test.screenshot.ViewScreenshotTestRule.Mode @RunWith(AndroidJUnit4::class) class FingerprintEnrollIntroScreenshotTest { - private val injector: Injector = Injector(FingerprintNavigationStep.Introduction) + private val injector: Injector = Injector(FingerprintNavigationStep.Introduction()) @Rule @JvmField