Remove FingerprintStateViewModel.
Since FingerprintStateViewModel is too general as a view model, this CL removes it and adds more concrete flows in FingerprintSettingsViewModel and FingerprintEnrollViewModel. Test: atest FingerprintManagerInteractorTest Test: atest FingerprintSettingsViewModelTest Test: Verified enroll/deletion/renaming/authentication flows on Settings Change-Id: I3a0662195c4989de0813b92bccda9d36a7f7e32a
This commit is contained in:
@@ -47,6 +47,12 @@ interface FingerprintManagerInteractor {
|
||||
/** Returns the max enrollable fingerprints, note during SUW this might be 1 */
|
||||
val maxEnrollableFingerprints: Flow<Int>
|
||||
|
||||
/** Returns true if a user can enroll a fingerprint false otherwise. */
|
||||
val canEnrollFingerprints: Flow<Boolean>
|
||||
|
||||
/** Retrieves the sensor properties of a device */
|
||||
val sensorPropertiesInternal: Flow<FingerprintSensorPropertiesInternal?>
|
||||
|
||||
/** Runs [FingerprintManager.authenticate] */
|
||||
suspend fun authenticate(): FingerprintAuthAttemptViewModel
|
||||
|
||||
@@ -60,9 +66,6 @@ interface FingerprintManagerInteractor {
|
||||
*/
|
||||
suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
|
||||
|
||||
/** Returns true if a user can enroll a fingerprint false otherwise. */
|
||||
fun canEnrollFingerprints(numFingerprints: Int): Flow<Boolean>
|
||||
|
||||
/**
|
||||
* Removes the given fingerprint, returning true if it was successfully removed and false
|
||||
* otherwise
|
||||
@@ -77,9 +80,6 @@ interface FingerprintManagerInteractor {
|
||||
|
||||
/** Indicates if the press to auth feature has been enabled */
|
||||
suspend fun pressToAuthEnabled(): Boolean
|
||||
|
||||
/** Retrieves the sensor properties of a device */
|
||||
suspend fun sensorPropertiesInternal(): List<FingerprintSensorPropertiesInternal>
|
||||
}
|
||||
|
||||
class FingerprintManagerInteractorImpl(
|
||||
@@ -120,8 +120,15 @@ class FingerprintManagerInteractorImpl(
|
||||
)
|
||||
}
|
||||
|
||||
override fun canEnrollFingerprints(numFingerprints: Int): Flow<Boolean> = flow {
|
||||
emit(numFingerprints < maxFingerprints)
|
||||
override val canEnrollFingerprints: Flow<Boolean> = flow {
|
||||
emit(
|
||||
fingerprintManager.getEnrolledFingerprints(applicationContext.userId).size < maxFingerprints
|
||||
)
|
||||
}
|
||||
|
||||
override val sensorPropertiesInternal = flow {
|
||||
val sensorPropertiesInternal = fingerprintManager.sensorPropertiesInternal
|
||||
emit(if (sensorPropertiesInternal.isEmpty()) null else sensorPropertiesInternal.first())
|
||||
}
|
||||
|
||||
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
|
||||
@@ -165,11 +172,6 @@ class FingerprintManagerInteractorImpl(
|
||||
it.resume(pressToAuthProvider())
|
||||
}
|
||||
|
||||
override suspend fun sensorPropertiesInternal(): List<FingerprintSensorPropertiesInternal> =
|
||||
suspendCancellableCoroutine {
|
||||
it.resume(fingerprintManager.sensorPropertiesInternal)
|
||||
}
|
||||
|
||||
override suspend fun authenticate(): FingerprintAuthAttemptViewModel =
|
||||
suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptViewModel> ->
|
||||
val authenticationCallback =
|
||||
|
@@ -16,18 +16,6 @@
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.shared.model
|
||||
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
|
||||
/** Represents the fingerprint data nad the relevant state. */
|
||||
data class FingerprintStateViewModel(
|
||||
val fingerprintViewModels: List<FingerprintViewModel>,
|
||||
val canEnroll: Boolean,
|
||||
val maxFingerprints: Int,
|
||||
val hasSideFps: Boolean,
|
||||
val pressToAuth: Boolean,
|
||||
val sensorProps: FingerprintSensorPropertiesInternal,
|
||||
)
|
||||
|
||||
data class FingerprintViewModel(
|
||||
val name: String,
|
||||
val fingerId: Int,
|
||||
|
@@ -47,10 +47,10 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Confirmation
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Enrollment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Finish
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Intro
|
||||
@@ -179,8 +179,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
)[FingerprintEnrollmentNavigationViewModel::class.java]
|
||||
|
||||
// Initialize FingerprintViewModel
|
||||
ViewModelProvider(this, FingerprintViewModel.FingerprintViewModelFactory(interactor))[
|
||||
FingerprintViewModel::class.java]
|
||||
ViewModelProvider(
|
||||
this,
|
||||
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(interactor)
|
||||
)[FingerprintEnrollViewModel::class.java]
|
||||
|
||||
// Initialize scroll view model
|
||||
ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
|
||||
|
@@ -34,10 +34,10 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.android.settings.R
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Unicorn
|
||||
import com.google.android.setupcompat.template.FooterBarMixin
|
||||
import com.google.android.setupcompat.template.FooterButton
|
||||
@@ -76,7 +76,7 @@ class FingerprintEnrollmentIntroV2Fragment : Fragment(R.layout.fingerprint_v2_en
|
||||
private lateinit var footerBarMixin: FooterBarMixin
|
||||
private lateinit var textModel: TextModel
|
||||
private lateinit var navigationViewModel: FingerprintEnrollmentNavigationViewModel
|
||||
private lateinit var fingerprintStateViewModel: FingerprintViewModel
|
||||
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
|
||||
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
|
||||
private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
|
||||
|
||||
@@ -84,8 +84,8 @@ class FingerprintEnrollmentIntroV2Fragment : Fragment(R.layout.fingerprint_v2_en
|
||||
super.onCreate(savedInstanceState)
|
||||
navigationViewModel =
|
||||
ViewModelProvider(requireActivity())[FingerprintEnrollmentNavigationViewModel::class.java]
|
||||
fingerprintStateViewModel =
|
||||
ViewModelProvider(requireActivity())[FingerprintViewModel::class.java]
|
||||
fingerprintEnrollViewModel =
|
||||
ViewModelProvider(requireActivity())[FingerprintEnrollViewModel::class.java]
|
||||
fingerprintScrollViewModel =
|
||||
ViewModelProvider(requireActivity())[FingerprintScrollViewModel::class.java]
|
||||
gateKeeperViewModel =
|
||||
@@ -98,13 +98,11 @@ class FingerprintEnrollmentIntroV2Fragment : Fragment(R.layout.fingerprint_v2_en
|
||||
lifecycleScope.launch {
|
||||
combine(
|
||||
navigationViewModel.enrollType,
|
||||
fingerprintStateViewModel.fingerprintStateViewModel,
|
||||
) { enrollType, fingerprintStateViewModel ->
|
||||
Pair(enrollType, fingerprintStateViewModel)
|
||||
fingerprintEnrollViewModel.sensorType,
|
||||
) { enrollType, sensorType ->
|
||||
Pair(enrollType, sensorType)
|
||||
}
|
||||
.collect { (enrollType, fingerprintStateViewModel) ->
|
||||
val sensorProps = fingerprintStateViewModel?.sensorProps
|
||||
|
||||
.collect { (enrollType, sensorType) ->
|
||||
textModel =
|
||||
when (enrollType) {
|
||||
Unicorn -> getUnicornTextModel()
|
||||
@@ -145,7 +143,7 @@ class FingerprintEnrollmentIntroV2Fragment : Fragment(R.layout.fingerprint_v2_en
|
||||
|
||||
val iconShield: ImageView = view.requireViewById(R.id.icon_shield)
|
||||
val footerMessage6: TextView = view.requireViewById(R.id.footer_message_6)
|
||||
when (sensorProps?.sensorType) {
|
||||
when (sensorType) {
|
||||
FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
|
||||
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL -> {
|
||||
footerMessage6.visibility = View.VISIBLE
|
||||
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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
|
||||
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractor
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.transform
|
||||
|
||||
/** Represents all of the fingerprint information needed for fingerprint enrollment. */
|
||||
class FingerprintEnrollViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) :
|
||||
ViewModel() {
|
||||
|
||||
/** Represents the stream of [FingerprintSensorProperties.SensorType] */
|
||||
val sensorType: Flow<Int> =
|
||||
fingerprintManagerInteractor.sensorPropertiesInternal.transform { it?.sensorType }
|
||||
|
||||
class FingerprintEnrollViewModelFactory(val interactor: FingerprintManagerInteractor) :
|
||||
ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>,
|
||||
): T {
|
||||
return FingerprintEnrollViewModel(interactor) as T
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintStateViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.last
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** Represents all of the fingerprint information needed for fingerprint enrollment. */
|
||||
class FingerprintViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) :
|
||||
ViewModel() {
|
||||
|
||||
private val _fingerprintViewModel: MutableStateFlow<FingerprintStateViewModel?> =
|
||||
MutableStateFlow(null)
|
||||
|
||||
/**
|
||||
* A flow that contains a [FingerprintStateViewModel] which contains the relevant information for
|
||||
* enrollment
|
||||
*/
|
||||
val fingerprintStateViewModel: Flow<FingerprintStateViewModel?> =
|
||||
_fingerprintViewModel.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val enrolledFingerprints =
|
||||
fingerprintManagerInteractor.enrolledFingerprints.last().map {
|
||||
com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel(
|
||||
it.name,
|
||||
it.fingerId,
|
||||
it.deviceId
|
||||
)
|
||||
}
|
||||
val sensorProps = fingerprintManagerInteractor.sensorPropertiesInternal().first()
|
||||
val maxFingerprints = 5
|
||||
_fingerprintViewModel.update {
|
||||
FingerprintStateViewModel(
|
||||
enrolledFingerprints,
|
||||
enrolledFingerprints.size < maxFingerprints,
|
||||
maxFingerprints,
|
||||
sensorProps.isAnySidefpsType,
|
||||
false,
|
||||
sensorProps,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FingerprintViewModelFactory(val interactor: FingerprintManagerInteractor) :
|
||||
ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>,
|
||||
): T {
|
||||
|
||||
return FingerprintViewModel(interactor) as T
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,7 +20,6 @@ import android.hardware.fingerprint.FingerprintManager
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintStateViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder.FingerprintView
|
||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollAdditionalFingerprint
|
||||
@@ -35,6 +34,7 @@ import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.Prefer
|
||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.ShowSettings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -70,7 +70,11 @@ object FingerprintSettingsViewBinder {
|
||||
/** Indicates what result should be set for the returning callee */
|
||||
fun setResultExternal(resultCode: Int)
|
||||
/** Indicates the settings UI should be shown */
|
||||
fun showSettings(state: FingerprintStateViewModel)
|
||||
fun showSettings(enrolledFingerprints: List<FingerprintViewModel>)
|
||||
/** Updates the add fingerprints preference */
|
||||
fun updateAddFingerprintsPreference(canEnroll: Boolean, maxFingerprints: Int)
|
||||
/** Updates the sfps fingerprints preference */
|
||||
fun updateSfpsPreference(isSfpsPrefVisible: Boolean)
|
||||
/** Indicates that a user has been locked out */
|
||||
fun userLockout(authAttemptViewModel: FingerprintAuthAttemptViewModel.Error)
|
||||
/** Indicates a fingerprint preference should be highlighted */
|
||||
@@ -93,9 +97,13 @@ object FingerprintSettingsViewBinder {
|
||||
/** Result listener for launching enrollments **after** a user has reached the settings page. */
|
||||
|
||||
// Settings display flow
|
||||
lifecycleScope.launch { viewModel.enrolledFingerprints.collect { view.showSettings(it) } }
|
||||
lifecycleScope.launch {
|
||||
viewModel.fingerprintState.filterNotNull().collect { view.showSettings(it) }
|
||||
viewModel.addFingerprintPrefInfo.collect { (enablePref, maxFingerprints) ->
|
||||
view.updateAddFingerprintsPreference(enablePref, maxFingerprints)
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch { viewModel.isSfpsPrefVisible.collect { view.updateSfpsPreference(it) } }
|
||||
|
||||
// Dialog flow
|
||||
lifecycleScope.launch {
|
||||
|
@@ -47,7 +47,6 @@ import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintStateViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
|
||||
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
|
||||
@@ -304,44 +303,52 @@ class FingerprintSettingsV2Fragment :
|
||||
settingsViewModel.onDeleteClicked(fingerprintViewModel)
|
||||
}
|
||||
|
||||
override fun showSettings(state: FingerprintStateViewModel) {
|
||||
override fun showSettings(enrolledFingerprints: List<FingerprintViewModel>) {
|
||||
val category =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINTS_ENROLLED_CATEGORY)
|
||||
as PreferenceCategory?
|
||||
|
||||
category?.removeAll()
|
||||
|
||||
state.fingerprintViewModels.forEach { fingerprint ->
|
||||
enrolledFingerprints.forEach { fingerprint ->
|
||||
category?.addPreference(
|
||||
FingerprintSettingsPreference(
|
||||
requireContext(),
|
||||
fingerprint,
|
||||
this@FingerprintSettingsV2Fragment,
|
||||
state.fingerprintViewModels.size == 1,
|
||||
enrolledFingerprints.size == 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
category?.isVisible = true
|
||||
|
||||
createFingerprintsFooterPreference(state.canEnroll, state.maxFingerprints)
|
||||
preferenceScreen.isVisible = true
|
||||
addFooter()
|
||||
}
|
||||
|
||||
override fun updateAddFingerprintsPreference(canEnroll: Boolean, maxFingerprints: Int) {
|
||||
val pref = this@FingerprintSettingsV2Fragment.findPreference<Preference>(KEY_FINGERPRINT_ADD)
|
||||
val maxSummary = context?.getString(R.string.fingerprint_add_max, maxFingerprints) ?: ""
|
||||
pref?.summary = maxSummary
|
||||
pref?.isEnabled = canEnroll
|
||||
pref?.setOnPreferenceClickListener {
|
||||
navigationViewModel.onAddFingerprintClicked()
|
||||
true
|
||||
}
|
||||
pref?.isVisible = true
|
||||
}
|
||||
|
||||
override fun updateSfpsPreference(isSfpsPrefVisible: Boolean) {
|
||||
val sideFpsPref =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_SIDE_FPS_CATEGORY)
|
||||
as PreferenceCategory?
|
||||
sideFpsPref?.isVisible = false
|
||||
|
||||
if (state.hasSideFps) {
|
||||
sideFpsPref?.isVisible = state.fingerprintViewModels.isNotEmpty()
|
||||
val otherPref =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(
|
||||
KEY_FINGERPRINT_SIDE_FPS_SCREEN_ON_TO_AUTH
|
||||
) as Preference?
|
||||
otherPref?.isVisible = state.fingerprintViewModels.isNotEmpty()
|
||||
}
|
||||
addFooter(state.hasSideFps)
|
||||
sideFpsPref?.isVisible = isSfpsPrefVisible
|
||||
val otherPref =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_SIDE_FPS_SCREEN_ON_TO_AUTH)
|
||||
as Preference?
|
||||
otherPref?.isVisible = isSfpsPrefVisible
|
||||
}
|
||||
private fun addFooter(hasSideFps: Boolean) {
|
||||
|
||||
private fun addFooter() {
|
||||
val footer =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINT_FOOTER)
|
||||
as PreferenceCategory?
|
||||
@@ -380,10 +387,8 @@ class FingerprintSettingsV2Fragment :
|
||||
footerColumns.add(column1)
|
||||
val column2 = FooterColumn()
|
||||
column2.title = getText(R.string.security_fingerprint_disclaimer_lockscreen_disabled_2)
|
||||
if (hasSideFps) {
|
||||
column2.learnMoreOverrideText =
|
||||
getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
|
||||
}
|
||||
column2.learnMoreOverrideText =
|
||||
getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
|
||||
column2.learnMoreOnClickListener = learnMoreClickListener
|
||||
footerColumns.add(column2)
|
||||
} else {
|
||||
@@ -394,10 +399,8 @@ class FingerprintSettingsV2Fragment :
|
||||
DeviceHelper.getDeviceName(requireActivity())
|
||||
)
|
||||
column.learnMoreOnClickListener = learnMoreClickListener
|
||||
if (hasSideFps) {
|
||||
column.learnMoreOverrideText =
|
||||
getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
|
||||
}
|
||||
column.learnMoreOverrideText =
|
||||
getText(R.string.security_settings_fingerprint_settings_footer_learn_more)
|
||||
footerColumns.add(column)
|
||||
}
|
||||
|
||||
@@ -550,18 +553,6 @@ class FingerprintSettingsV2Fragment :
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFingerprintsFooterPreference(canEnroll: Boolean, maxFingerprints: Int) {
|
||||
val pref = this@FingerprintSettingsV2Fragment.findPreference<Preference>(KEY_FINGERPRINT_ADD)
|
||||
val maxSummary = context?.getString(R.string.fingerprint_add_max, maxFingerprints) ?: ""
|
||||
pref?.summary = maxSummary
|
||||
pref?.isEnabled = canEnroll
|
||||
pref?.setOnPreferenceClickListener {
|
||||
navigationViewModel.onAddFingerprintClicked()
|
||||
true
|
||||
}
|
||||
pref?.isVisible = true
|
||||
}
|
||||
|
||||
private fun fingerprintPreferences(): List<FingerprintSettingsPreference?> {
|
||||
val category =
|
||||
this@FingerprintSettingsV2Fragment.findPreference(KEY_FINGERPRINTS_ENROLLED_CATEGORY)
|
||||
|
@@ -18,26 +18,27 @@ package com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel
|
||||
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintStateViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.last
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -52,13 +53,30 @@ class FingerprintSettingsViewModel(
|
||||
private val backgroundDispatcher: CoroutineDispatcher,
|
||||
private val navigationViewModel: FingerprintSettingsNavigationViewModel,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _consumerShouldAuthenticate: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
|
||||
private val fingerprintSensorPropertiesInternal:
|
||||
MutableStateFlow<List<FingerprintSensorPropertiesInternal>?> =
|
||||
private val _enrolledFingerprints: MutableStateFlow<List<FingerprintViewModel>?> =
|
||||
MutableStateFlow(null)
|
||||
|
||||
/** Represents the stream of enrolled fingerprints. */
|
||||
val enrolledFingerprints: Flow<List<FingerprintViewModel>> =
|
||||
_enrolledFingerprints.asStateFlow().filterNotNull().filterOnlyWhenSettingsIsShown()
|
||||
|
||||
/** Represents the stream of the information of "Add Fingerprint" preference. */
|
||||
val addFingerprintPrefInfo: Flow<Pair<Boolean, Int>> =
|
||||
_enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform {
|
||||
emit(
|
||||
Pair(
|
||||
fingerprintManagerInteractor.canEnrollFingerprints.first(),
|
||||
fingerprintManagerInteractor.maxEnrollableFingerprints.first()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Represents the stream of visibility of sfps preference. */
|
||||
val isSfpsPrefVisible: Flow<Boolean> =
|
||||
_enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform {
|
||||
emit(fingerprintManagerInteractor.hasSideFps() && !it.isNullOrEmpty())
|
||||
}
|
||||
|
||||
private val _isShowingDialog: MutableStateFlow<PreferenceViewModel?> = MutableStateFlow(null)
|
||||
val isShowingDialog =
|
||||
_isShowingDialog.combine(navigationViewModel.nextStep) { dialogFlow, nextStep ->
|
||||
@@ -69,16 +87,13 @@ class FingerprintSettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private val _fingerprintStateViewModel: MutableStateFlow<FingerprintStateViewModel?> =
|
||||
MutableStateFlow(null)
|
||||
val fingerprintState: Flow<FingerprintStateViewModel?> =
|
||||
_fingerprintStateViewModel.combineTransform(navigationViewModel.nextStep) {
|
||||
settingsShowingViewModel,
|
||||
currStep ->
|
||||
if (currStep != null && currStep is ShowSettings) {
|
||||
emit(settingsShowingViewModel)
|
||||
}
|
||||
}
|
||||
private val _consumerShouldAuthenticate: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
|
||||
private val _fingerprintSensorType: Flow<Int> =
|
||||
fingerprintManagerInteractor.sensorPropertiesInternal.transform { it?.sensorType }
|
||||
|
||||
private val _sensorNullOrEmpty: Flow<Boolean> =
|
||||
fingerprintManagerInteractor.sensorPropertiesInternal.map{it ==null}
|
||||
|
||||
private val _isLockedOut: MutableStateFlow<FingerprintAuthAttemptViewModel.Error?> =
|
||||
MutableStateFlow(null)
|
||||
@@ -86,7 +101,7 @@ class FingerprintSettingsViewModel(
|
||||
private val _authSucceeded: MutableSharedFlow<FingerprintAuthAttemptViewModel.Success?> =
|
||||
MutableSharedFlow()
|
||||
|
||||
private val attemptsSoFar: MutableStateFlow<Int> = MutableStateFlow(0)
|
||||
private val _attemptsSoFar: MutableStateFlow<Int> = MutableStateFlow(0)
|
||||
/**
|
||||
* This is a very tricky flow. The current fingerprint manager APIs are not robust, and a proper
|
||||
* implementation would take quite a lot of code to implement, it might be easier to rewrite
|
||||
@@ -101,11 +116,20 @@ class FingerprintSettingsViewModel(
|
||||
_isShowingDialog,
|
||||
navigationViewModel.nextStep,
|
||||
_consumerShouldAuthenticate,
|
||||
_fingerprintStateViewModel,
|
||||
_enrolledFingerprints,
|
||||
_isLockedOut,
|
||||
attemptsSoFar,
|
||||
fingerprintSensorPropertiesInternal
|
||||
) { dialogShowing, step, resume, fingerprints, isLockedOut, attempts, sensorProps ->
|
||||
_attemptsSoFar,
|
||||
_fingerprintSensorType,
|
||||
_sensorNullOrEmpty
|
||||
) {
|
||||
dialogShowing,
|
||||
step,
|
||||
resume,
|
||||
fingerprints,
|
||||
isLockedOut,
|
||||
attempts,
|
||||
sensorType,
|
||||
sensorNullOrEmpty ->
|
||||
if (DEBUG) {
|
||||
Log.d(
|
||||
TAG,
|
||||
@@ -115,13 +139,13 @@ class FingerprintSettingsViewModel(
|
||||
"fingerprints=${fingerprints}," +
|
||||
"lockedOut=${isLockedOut}," +
|
||||
"attempts=${attempts}," +
|
||||
"sensorProps=${sensorProps}"
|
||||
"sensorType=${sensorType}" +
|
||||
"sensorNullOrEmpty=${sensorNullOrEmpty}"
|
||||
)
|
||||
}
|
||||
if (sensorProps.isNullOrEmpty()) {
|
||||
if (sensorNullOrEmpty) {
|
||||
return@combine false
|
||||
}
|
||||
val sensorType = sensorProps[0].sensorType
|
||||
if (
|
||||
listOf(
|
||||
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
|
||||
@@ -133,7 +157,7 @@ class FingerprintSettingsViewModel(
|
||||
}
|
||||
|
||||
if (step != null && step is ShowSettings) {
|
||||
if (fingerprints?.fingerprintViewModels?.isNotEmpty() == true) {
|
||||
if (fingerprints?.isNotEmpty() == true) {
|
||||
return@combine dialogShowing == null && isLockedOut == null && resume && attempts < 15
|
||||
}
|
||||
}
|
||||
@@ -172,18 +196,12 @@ class FingerprintSettingsViewModel(
|
||||
.flowOn(backgroundDispatcher)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
fingerprintSensorPropertiesInternal.update {
|
||||
fingerprintManagerInteractor.sensorPropertiesInternal()
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
navigationViewModel.nextStep.filterNotNull().collect {
|
||||
_isShowingDialog.update { null }
|
||||
if (it is ShowSettings) {
|
||||
// reset state
|
||||
updateSettingsData()
|
||||
updateEnrolledFingerprints()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,7 +218,7 @@ class FingerprintSettingsViewModel(
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "userId: $userId\n" + "fingerprintState: ${_fingerprintStateViewModel.value}\n"
|
||||
return "userId: $userId\n" + "enrolledFingerprints: ${_enrolledFingerprints.value}\n"
|
||||
}
|
||||
|
||||
/** The fingerprint delete button has been clicked. */
|
||||
@@ -229,7 +247,7 @@ class FingerprintSettingsViewModel(
|
||||
fun deleteFingerprint(fp: FingerprintViewModel) {
|
||||
viewModelScope.launch(backgroundDispatcher) {
|
||||
if (fingerprintManagerInteractor.removeFingerprint(fp)) {
|
||||
updateSettingsData()
|
||||
updateEnrolledFingerprints()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,45 +256,25 @@ class FingerprintSettingsViewModel(
|
||||
fun renameFingerprint(fp: FingerprintViewModel, newName: String) {
|
||||
viewModelScope.launch {
|
||||
fingerprintManagerInteractor.renameFingerprint(fp, newName)
|
||||
updateSettingsData()
|
||||
updateEnrolledFingerprints()
|
||||
}
|
||||
}
|
||||
|
||||
private fun attemptingAuth() {
|
||||
attemptsSoFar.update { it + 1 }
|
||||
_attemptsSoFar.update { it + 1 }
|
||||
}
|
||||
|
||||
private suspend fun onAuthSuccess(success: FingerprintAuthAttemptViewModel.Success) {
|
||||
_authSucceeded.emit(success)
|
||||
attemptsSoFar.update { 0 }
|
||||
_attemptsSoFar.update { 0 }
|
||||
}
|
||||
|
||||
private fun lockout(attemptViewModel: FingerprintAuthAttemptViewModel.Error) {
|
||||
_isLockedOut.update { attemptViewModel }
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is sort of a hack, it's used whenever we want to check for fingerprint state
|
||||
* updates.
|
||||
*/
|
||||
private suspend fun updateSettingsData() {
|
||||
Log.d(TAG, "update settings data called")
|
||||
val fingerprints = fingerprintManagerInteractor.enrolledFingerprints.last()
|
||||
val canEnrollFingerprint =
|
||||
fingerprintManagerInteractor.canEnrollFingerprints(fingerprints.size).last()
|
||||
val maxFingerprints = fingerprintManagerInteractor.maxEnrollableFingerprints.last()
|
||||
val hasSideFps = fingerprintManagerInteractor.hasSideFps()
|
||||
val pressToAuthEnabled = fingerprintManagerInteractor.pressToAuthEnabled()
|
||||
_fingerprintStateViewModel.update {
|
||||
FingerprintStateViewModel(
|
||||
fingerprints,
|
||||
canEnrollFingerprint,
|
||||
maxFingerprints,
|
||||
hasSideFps,
|
||||
pressToAuthEnabled,
|
||||
fingerprintManagerInteractor.sensorPropertiesInternal().first(),
|
||||
)
|
||||
}
|
||||
private suspend fun updateEnrolledFingerprints() {
|
||||
_enrolledFingerprints.update { fingerprintManagerInteractor.enrolledFingerprints.first() }
|
||||
}
|
||||
|
||||
/** Used to indicate whether the consumer of the view model is ready for authentication. */
|
||||
@@ -284,6 +282,13 @@ class FingerprintSettingsViewModel(
|
||||
_consumerShouldAuthenticate.update { authenticate }
|
||||
}
|
||||
|
||||
private fun <T> Flow<T>.filterOnlyWhenSettingsIsShown() =
|
||||
combineTransform(navigationViewModel.nextStep) { value, currStep ->
|
||||
if (currStep != null && currStep is ShowSettings) {
|
||||
emit(value)
|
||||
}
|
||||
}
|
||||
|
||||
class FingerprintSettingsViewModelFactory(
|
||||
private val userId: Int,
|
||||
private val interactor: FingerprintManagerInteractor,
|
||||
@@ -307,7 +312,7 @@ class FingerprintSettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
|
||||
private inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(
|
||||
flow: Flow<T1>,
|
||||
flow2: Flow<T2>,
|
||||
flow3: Flow<T3>,
|
||||
@@ -315,9 +320,10 @@ private inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
|
||||
flow5: Flow<T5>,
|
||||
flow6: Flow<T6>,
|
||||
flow7: Flow<T7>,
|
||||
crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R
|
||||
flow8: Flow<T8>,
|
||||
crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R
|
||||
): Flow<R> {
|
||||
return combine(flow, flow2, flow3, flow4, flow5, flow6, flow7) { args: Array<*> ->
|
||||
return combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) { args: Array<*> ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
transform(
|
||||
args[0] as T1,
|
||||
@@ -327,6 +333,7 @@ private inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
|
||||
args[4] as T5,
|
||||
args[5] as T6,
|
||||
args[6] as T7,
|
||||
args[7] as T8,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Fingerprin
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
/** Fake to be used by other classes to easily fake the FingerprintManager implementation. */
|
||||
class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
|
||||
@@ -53,15 +53,16 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
|
||||
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> {
|
||||
return challengeToGenerate
|
||||
}
|
||||
override val enrolledFingerprints: Flow<List<FingerprintViewModel>> = flow {
|
||||
emit(enrolledFingerprintsInternal)
|
||||
}
|
||||
override val enrolledFingerprints: Flow<List<FingerprintViewModel>> =
|
||||
flowOf(enrolledFingerprintsInternal)
|
||||
|
||||
override fun canEnrollFingerprints(numFingerprints: Int): Flow<Boolean> = flow {
|
||||
emit(numFingerprints < enrollableFingerprints)
|
||||
}
|
||||
override val canEnrollFingerprints: Flow<Boolean> =
|
||||
flowOf(enrolledFingerprintsInternal.size < enrollableFingerprints)
|
||||
|
||||
override val maxEnrollableFingerprints: Flow<Int> = flow { emit(enrollableFingerprints) }
|
||||
override val sensorPropertiesInternal: Flow<FingerprintSensorPropertiesInternal?> =
|
||||
flowOf(sensorProps.first())
|
||||
|
||||
override val maxEnrollableFingerprints: Flow<Int> = flowOf(enrollableFingerprints)
|
||||
|
||||
override suspend fun removeFingerprint(fp: FingerprintViewModel): Boolean {
|
||||
return enrolledFingerprintsInternal.remove(fp)
|
||||
@@ -80,7 +81,4 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
|
||||
override suspend fun pressToAuthEnabled(): Boolean {
|
||||
return pressToAuthEnabled
|
||||
}
|
||||
|
||||
override suspend fun sensorPropertiesInternal(): List<FingerprintSensorPropertiesInternal> =
|
||||
sensorProps
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ package com.android.settings.fingerprint2.domain.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.hardware.fingerprint.Fingerprint
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintManager.CryptoObject
|
||||
@@ -51,8 +50,11 @@ import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.ArgumentMatchers.nullable
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoJUnitRunner
|
||||
import org.mockito.stubbing.OngoingStubbing
|
||||
|
||||
@RunWith(MockitoJUnitRunner::class)
|
||||
class FingerprintManagerInteractorTest {
|
||||
@@ -82,8 +84,7 @@ class FingerprintManagerInteractorTest {
|
||||
@Test
|
||||
fun testEmptyFingerprints() =
|
||||
testScope.runTest {
|
||||
Mockito.`when`(fingerprintManager.getEnrolledFingerprints(Mockito.anyInt()))
|
||||
.thenReturn(emptyList())
|
||||
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(emptyList())
|
||||
|
||||
val emptyFingerprintList: List<Fingerprint> = emptyList()
|
||||
assertThat(underTest.enrolledFingerprints.last()).isEqualTo(emptyFingerprintList)
|
||||
@@ -94,8 +95,7 @@ class FingerprintManagerInteractorTest {
|
||||
testScope.runTest {
|
||||
val expected = Fingerprint("Finger 1,", 2, 3L)
|
||||
val fingerprintList: List<Fingerprint> = listOf(expected)
|
||||
Mockito.`when`(fingerprintManager.getEnrolledFingerprints(Mockito.anyInt()))
|
||||
.thenReturn(fingerprintList)
|
||||
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
|
||||
|
||||
val list = underTest.enrolledFingerprints.last()
|
||||
assertThat(list.size).isEqualTo(fingerprintList.size)
|
||||
@@ -108,21 +108,22 @@ class FingerprintManagerInteractorTest {
|
||||
@Test
|
||||
fun testCanEnrollFingerprint() =
|
||||
testScope.runTest {
|
||||
val mockContext = Mockito.mock(Context::class.java)
|
||||
val resources = Mockito.mock(Resources::class.java)
|
||||
Mockito.`when`(mockContext.resources).thenReturn(resources)
|
||||
Mockito.`when`(resources.getInteger(anyInt())).thenReturn(3)
|
||||
underTest =
|
||||
FingerprintManagerInteractorImpl(
|
||||
mockContext,
|
||||
backgroundDispatcher,
|
||||
fingerprintManager,
|
||||
gateKeeperPasswordProvider,
|
||||
pressToAuthProvider,
|
||||
val fingerprintList1: List<Fingerprint> =
|
||||
listOf(
|
||||
Fingerprint("Finger 1,", 2, 3L),
|
||||
Fingerprint("Finger 2,", 3, 3L),
|
||||
Fingerprint("Finger 3,", 4, 3L)
|
||||
)
|
||||
val fingerprintList2: List<Fingerprint> =
|
||||
fingerprintList1.plus(
|
||||
listOf(Fingerprint("Finger 4,", 5, 3L), Fingerprint("Finger 5,", 6, 3L))
|
||||
)
|
||||
whenever(fingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(fingerprintList1)
|
||||
.thenReturn(fingerprintList2)
|
||||
|
||||
assertThat(underTest.canEnrollFingerprints(2).last()).isTrue()
|
||||
assertThat(underTest.canEnrollFingerprints(3).last()).isFalse()
|
||||
assertThat(underTest.canEnrollFingerprints.last()).isTrue()
|
||||
assertThat(underTest.canEnrollFingerprints.last()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,7 +133,7 @@ class FingerprintManagerInteractorTest {
|
||||
val challenge = 100L
|
||||
val intent = Intent()
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, challenge)
|
||||
Mockito.`when`(
|
||||
whenever(
|
||||
gateKeeperPasswordProvider.requestGatekeeperHat(
|
||||
any(Intent::class.java),
|
||||
anyLong(),
|
||||
@@ -148,8 +149,7 @@ class FingerprintManagerInteractorTest {
|
||||
val job = testScope.launch { result = underTest.generateChallenge(1L) }
|
||||
runCurrent()
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
.generateChallenge(anyInt(), capture(generateChallengeCallback))
|
||||
verify(fingerprintManager).generateChallenge(anyInt(), capture(generateChallengeCallback))
|
||||
generateChallengeCallback.value.onChallengeGenerated(1, 2, challenge)
|
||||
|
||||
runCurrent()
|
||||
@@ -173,7 +173,7 @@ class FingerprintManagerInteractorTest {
|
||||
testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) }
|
||||
runCurrent()
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
verify(fingerprintManager)
|
||||
.remove(any(Fingerprint::class.java), anyInt(), capture(removalCallback))
|
||||
removalCallback.value.onRemovalSucceeded(fingerprintToRemove, 1)
|
||||
|
||||
@@ -197,7 +197,7 @@ class FingerprintManagerInteractorTest {
|
||||
testScope.launch { result = underTest.removeFingerprint(fingerprintViewModelToRemove) }
|
||||
runCurrent()
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
verify(fingerprintManager)
|
||||
.remove(any(Fingerprint::class.java), anyInt(), capture(removalCallback))
|
||||
removalCallback.value.onRemovalError(
|
||||
fingerprintToRemove,
|
||||
@@ -218,8 +218,7 @@ class FingerprintManagerInteractorTest {
|
||||
|
||||
underTest.renameFingerprint(fingerprintToRename, "Woo")
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
.rename(eq(fingerprintToRename.fingerId), anyInt(), safeEq("Woo"))
|
||||
verify(fingerprintManager).rename(eq(fingerprintToRename.fingerId), anyInt(), safeEq("Woo"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,7 +234,7 @@ class FingerprintManagerInteractorTest {
|
||||
|
||||
runCurrent()
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
verify(fingerprintManager)
|
||||
.authenticate(
|
||||
nullable(CryptoObject::class.java),
|
||||
any(CancellationSignal::class.java),
|
||||
@@ -263,7 +262,7 @@ class FingerprintManagerInteractorTest {
|
||||
|
||||
runCurrent()
|
||||
|
||||
Mockito.verify(fingerprintManager)
|
||||
verify(fingerprintManager)
|
||||
.authenticate(
|
||||
nullable(CryptoObject::class.java),
|
||||
any(CancellationSignal::class.java),
|
||||
@@ -284,4 +283,5 @@ class FingerprintManagerInteractorTest {
|
||||
private fun <T : Any> safeEq(value: T): T = eq(value) ?: value
|
||||
private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
|
||||
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
|
||||
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
|
||||
}
|
||||
|
Reference in New Issue
Block a user