1 /* <lambda>null2 * Copyright (C) 2023 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.viewmodel 18 19 import android.util.Log 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.lib.domain.interactor.FingerprintManagerInteractor 27 import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow 28 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish 29 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep 30 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.UiStep 31 import java.lang.NullPointerException 32 import kotlin.reflect.KClass 33 import kotlinx.coroutines.flow.Flow 34 import kotlinx.coroutines.flow.MutableStateFlow 35 import kotlinx.coroutines.flow.SharingStarted 36 import kotlinx.coroutines.flow.StateFlow 37 import kotlinx.coroutines.flow.asStateFlow 38 import kotlinx.coroutines.flow.combine 39 import kotlinx.coroutines.flow.combineTransform 40 import kotlinx.coroutines.flow.filterNotNull 41 import kotlinx.coroutines.flow.stateIn 42 import kotlinx.coroutines.flow.update 43 44 /** 45 * This class is essentially a wrapper around [FingerprintNavigationStep] that will be used by 46 * fragments/viewmodels that want to consume these events. It should provide no additional 47 * functionality beyond what is available in [FingerprintNavigationStep]. 48 */ 49 class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) : 50 ViewModel() { 51 52 private val _flowInternal: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null) 53 private val _hasConfirmedDeviceCredential: MutableStateFlow<Boolean> = MutableStateFlow(false) 54 private val _navStateInternal: StateFlow<NavigationState?> = 55 combine( 56 _flowInternal, 57 _hasConfirmedDeviceCredential, 58 fingerprintManagerInteractor.sensorPropertiesInternal, 59 ) { flow, hasConfirmed, sensorType -> 60 if (flow == null || sensorType == null) { 61 return@combine null 62 } 63 return@combine NavigationState(flow, hasConfirmed, sensorType) 64 } 65 .stateIn(viewModelScope, SharingStarted.Eagerly, null) 66 67 private var _currentStep = 68 MutableStateFlow<FingerprintNavigationStep?>(FingerprintNavigationStep.Init) 69 70 private var _navigateTo: MutableStateFlow<UiStep?> = MutableStateFlow(null) 71 val navigateTo: Flow<UiStep?> = _navigateTo.asStateFlow() 72 73 /** 74 * This indicates a navigation event should occur. Navigation depends on navStateInternal being 75 * present. 76 */ 77 val currentStep: Flow<FingerprintNavigationStep> = 78 _currentStep.filterNotNull().combineTransform(_navStateInternal.filterNotNull()) { navigation, _ 79 -> 80 emit(navigation) 81 } 82 83 private var _finishState = MutableStateFlow<Finish?>(null) 84 85 /** This indicates the activity should finish. */ 86 val shouldFinish: Flow<Finish?> = _finishState.asStateFlow() 87 88 private var _currentScreen = MutableStateFlow<UiStep?>(null) 89 90 /** This indicates what screen should currently be presenting to the user. */ 91 val currentScreen: Flow<UiStep?> = _currentScreen.asStateFlow() 92 93 /** Updates the type of flow the navigation should begin */ 94 fun updateFingerprintFlow(flow: FingerprintFlow) { 95 _flowInternal.update { flow } 96 } 97 98 /** Indicates if we have confirmed device credential */ 99 fun hasConfirmedDeviceCredential(hasConfirmedDeviceCredential: Boolean) { 100 _hasConfirmedDeviceCredential.update { hasConfirmedDeviceCredential } 101 } 102 103 /** See [updateInternal] for more details */ 104 fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) { 105 Log.d(TAG, "$caller.update($action) $debugStr") 106 val currentStep = _currentStep.value 107 val isUiStep = currentStep is UiStep && caller is UiStep 108 if (currentStep == null) { 109 throw NullPointerException("current step is null") 110 } 111 if (isUiStep && currentStep::class != caller) { 112 throw IllegalAccessError( 113 "Error $currentStep != $caller, $caller should not be sending any events at this time" 114 ) 115 } 116 val navState = _navStateInternal.value 117 if (navState == null) { 118 throw NullPointerException("nav state is null") 119 } 120 val nextStep = currentStep.update(navState, action) ?: return 121 Log.d(TAG, "nextStep=$nextStep") 122 // Whenever an state update occurs, everything should be cleared. 123 _currentStep.update { nextStep } 124 _finishState.update { null } 125 _currentScreen.update { null } 126 127 when (nextStep) { 128 is TransitionStep -> { 129 _navigateTo.update { nextStep.nextUiStep } 130 } 131 is Finish -> { 132 _finishState.update { nextStep } 133 } 134 is UiStep -> { 135 _currentScreen.update { nextStep } 136 } 137 } 138 } 139 140 companion object { 141 private const val TAG = "FingerprintNavigationViewModel" 142 val Factory: ViewModelProvider.Factory = viewModelFactory { 143 initializer { 144 val settingsApplication = 145 this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication 146 val biometricEnvironment = settingsApplication.biometricEnvironment 147 FingerprintNavigationViewModel(biometricEnvironment!!.fingerprintManagerInteractor) 148 } 149 } 150 } 151 } 152