1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
18 
19 import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
20 import androidx.lifecycle.ViewModel
21 import androidx.lifecycle.ViewModelProvider
22 import androidx.lifecycle.viewModelScope
23 import androidx.lifecycle.viewmodel.initializer
24 import androidx.lifecycle.viewmodel.viewModelFactory
25 import com.android.settings.SettingsApplication
26 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
27 import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
28 import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
29 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
30 import kotlinx.coroutines.flow.MutableStateFlow
31 import kotlinx.coroutines.flow.distinctUntilChanged
32 import kotlinx.coroutines.flow.filter
33 import kotlinx.coroutines.flow.filterIsInstance
34 import kotlinx.coroutines.flow.filterNotNull
35 import kotlinx.coroutines.flow.take
36 import kotlinx.coroutines.flow.toList
37 import kotlinx.coroutines.flow.transform
38 import kotlinx.coroutines.flow.update
39 import kotlinx.coroutines.launch
40 
41 /**
42  * This class is responsible for determining if we should animate the last step of an enrollment. It
43  * seems to be due to poor hardware implementation that the last step of a UDFPS enrollment takes
44  * much longer then normal (likely due to to saving the whole enrollment/or some kind of
45  * verification)
46  *
47  * Because of this, we will use the information of if a fingerprint was acquired(good) + enrollment
48  * has reached the last step
49  */
50 class UdfpsLastStepViewModel(
51   private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
52   private val vibrationInteractor: VibrationInteractor,
53 ) : ViewModel() {
54 
55   private val lastStep: MutableStateFlow<FingerEnrollState.EnrollProgress?> = MutableStateFlow(null)
56   private val stepSize: MutableStateFlow<Int?> = MutableStateFlow(null)
57 
58   init {
59     viewModelScope.launch {
60       val steps =
61         fingerprintEnrollEnrollingViewModel.enrollFlow
62           .filterIsInstance<FingerEnrollState.EnrollProgress>()
63           .distinctUntilChanged()
64           .take(2)
65           .toList()
66       stepSize.update { steps[0].remainingSteps - steps[1].remainingSteps }
67       lastStep.update { FingerEnrollState.EnrollProgress(0, steps[0].totalStepsRequired) }
68     }
69   }
70 
71   private val enrollProgress =
72     fingerprintEnrollEnrollingViewModel.enrollFlow.filterIsInstance<
73       FingerEnrollState.EnrollProgress
74     >()
75 
76   /** Indicates if we should animate the completion of udfps enrollment. */
77   val shouldAnimateCompletion =
78     enrollProgress
79       .transform { it ->
80         if (it.remainingSteps == stepSize.value) {
81           fingerprintEnrollEnrollingViewModel.enrollFlow
82             .filterIsInstance<FingerEnrollState.Acquired>()
83             .filter { acquired -> acquired.acquiredGood }
84             .collect {
85               vibrationInteractor.vibrate(
86                 FingerprintVibrationEffects.UdfpsSuccess,
87                 "UdfpsLastStepViewModel",
88               )
89 
90               emit(lastStep.value)
91             }
92         }
93       }
94       .filterNotNull()
95 
96   companion object {
97     val Factory: ViewModelProvider.Factory = viewModelFactory {
98       initializer {
99         val settingsApplication =
100           this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
101         val biometricEnvironment = settingsApplication.biometricEnvironment
102         val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
103         UdfpsLastStepViewModel(
104           provider[FingerprintEnrollEnrollingViewModel::class],
105           biometricEnvironment!!.vibrationInteractor,
106         )
107       }
108     }
109   }
110 }
111