Updating navigation view model

Test: atest com.android.settings.fingerprint2.ui.enrollment.modules.enrolling.rfps.viewmodel
com.android.settings.fingerprint2.ui.enrollment.viewmodel
com.android.settings.fingerprint2.ui.settings
com.android.settings.fingerprint2.domain.interactor
com.android.settings.fingerprint2.enrollment.viewmodel
Bug: 295205754

Change-Id: I210712ab76050b89452fb871cd2a4fb28bfd4012
This commit is contained in:
Joshua McCloskey
2023-11-01 20:19:30 +00:00
parent 9531458ca5
commit 98374376cc
53 changed files with 1132 additions and 790 deletions

View File

@@ -57,7 +57,7 @@ android_library {
"src/**/*.kt",
],
exclude_srcs: [
"src/com/android/settings/biometrics/fingerprint2/shared/**/*.kt",
"src/com/android/settings/biometrics/fingerprint2/lib/**/*.kt",
],
use_resource_processor: true,
resource_dirs: [

View File

@@ -20,8 +20,8 @@ import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERR
import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS
import android.hardware.fingerprint.FingerprintManager
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
object Util {
fun EnrollReason.toOriginalReason(): Int {
@@ -71,6 +71,4 @@ object Util {
this == FINGERPRINT_ERROR_CANCELED,
)
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.data.repository
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.biometrics.SensorProperties
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/**
* Provides the [FingerprintSensor]
*
* TODO(b/313493336): Move this to systemui
*/
interface FingerprintSensorRepo {
/** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor>
}
class FingerprintSensorRepoImpl(
fingerprintManager: FingerprintManager,
backgroundDispatcher: CoroutineDispatcher,
activityScope: CoroutineScope,
) : FingerprintSensorRepo {
override val fingerprintSensor: Flow<FingerprintSensor> =
callbackFlow {
val callback =
object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
override fun onAllAuthenticatorsRegistered(
sensors: List<FingerprintSensorPropertiesInternal>
) {
if (sensors.isEmpty()) {
trySend(DEFAULT_PROPS)
} else {
trySend(sensors[0].toFingerprintSensor())
}
}
}
withContext(backgroundDispatcher) {
fingerprintManager?.addAuthenticatorsRegisteredCallback(callback)
}
awaitClose {}
}
.stateIn(activityScope, started = SharingStarted.Eagerly, initialValue = DEFAULT_PROPS)
companion object {
private const val TAG = "FingerprintSensorRepoImpl"
private val DEFAULT_PROPS =
FingerprintSensorPropertiesInternal(
-1 /* sensorId */,
SensorProperties.STRENGTH_CONVENIENCE,
0 /* maxEnrollmentsPerUser */,
listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_UNKNOWN,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* 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.
@@ -14,13 +14,24 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.repository
package com.android.settings.biometrics.fingerprint2.data.repository
import android.content.Context
import android.provider.Settings
import com.android.settings.biometrics.fingerprint2.shared.data.repository.PressToAuthProvider
class PressToAuthProviderImpl(val context: Context) : PressToAuthProvider {
/** Interface that indicates if press to auth is on or off. */
interface PressToAuthRepo {
/** Indicates true if the PressToAuth feature is enabled, false otherwise. */
val isEnabled: Boolean
}
/** Indicates whether or not the press to auth feature is enabled. */
class PressToAuthRepoImpl(private val context: Context) : PressToAuthRepo {
/**
* Gets the status of the press to auth feature.
*
* Returns whether or not the press to auth feature is enabled.
*/
override val isEnabled: Boolean
get() {
var toReturn: Int =
@@ -43,7 +54,7 @@ class PressToAuthProviderImpl(val context: Context) : PressToAuthProvider {
context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn,
context.userId
context.userId,
)
}
return (toReturn == 1)

View File

@@ -26,16 +26,16 @@ import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
import com.android.settings.biometrics.fingerprint2.shared.data.repository.PressToAuthProvider
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.shared.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepo
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepo
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.password.ChooseLockSettingsHelper
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CancellableContinuation
@@ -57,8 +57,9 @@ class FingerprintManagerInteractorImpl(
applicationContext: Context,
private val backgroundDispatcher: CoroutineDispatcher,
private val fingerprintManager: FingerprintManager,
fingerprintSensorRepo: FingerprintSensorRepo,
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val pressToAuthProvider: PressToAuthProvider,
private val pressToAuthRepo: PressToAuthRepo,
private val fingerprintFlow: FingerprintFlow,
) : FingerprintManagerInteractor {
@@ -100,13 +101,7 @@ class FingerprintManagerInteractorImpl(
)
}
override val sensorPropertiesInternal = flow {
val sensorPropertiesInternal = fingerprintManager.sensorPropertiesInternal
emit(
if (sensorPropertiesInternal.isEmpty()) null
else sensorPropertiesInternal.first().toFingerprintSensor()
)
}
override val sensorPropertiesInternal = fingerprintSensorRepo.fingerprintSensor
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
@@ -136,8 +131,7 @@ class FingerprintManagerInteractorImpl(
totalSteps = remaining + 1
}
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure {
error ->
trySend(FingerEnrollState.EnrollProgress(remaining, totalSteps!!)).onFailure { error ->
Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
}
@@ -148,13 +142,16 @@ class FingerprintManagerInteractorImpl(
}
override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString()))
.onFailure { error -> Log.d(TAG, "onEnrollmentHelp failed to send, due to $error") }
trySend(FingerEnrollState.EnrollHelp(helpMsgId, helpString.toString())).onFailure { error
->
Log.d(TAG, "onEnrollmentHelp failed to send, due to $error")
}
}
override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard))
.onFailure { error -> Log.d(TAG, "onEnrollmentError failed to send, due to $error") }
trySend(errMsgId.toEnrollError(fingerprintFlow == SetupWizard)).onFailure { error ->
Log.d(TAG, "onEnrollmentError failed to send, due to $error")
}
Log.d(TAG, "onEnrollmentError($errMsgId)")
streamEnded = true
enrollRequestOutstanding.update { false }
@@ -185,14 +182,14 @@ class FingerprintManagerInteractorImpl(
override fun onRemovalError(
fp: android.hardware.fingerprint.Fingerprint,
errMsgId: Int,
errString: CharSequence
errString: CharSequence,
) {
it.resume(false)
}
override fun onRemovalSucceeded(
fp: android.hardware.fingerprint.Fingerprint?,
remaining: Int
remaining: Int,
) {
it.resume(true)
}
@@ -200,7 +197,7 @@ class FingerprintManagerInteractorImpl(
fingerprintManager.remove(
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
applicationContext.userId,
callback
callback,
)
}
@@ -215,7 +212,7 @@ class FingerprintManagerInteractorImpl(
}
override suspend fun pressToAuthEnabled(): Boolean = suspendCancellableCoroutine {
it.resume(pressToAuthProvider.isEnabled)
it.resume(pressToAuthRepo.isEnabled)
}
override suspend fun authenticate(): FingerprintAuthAttemptModel =
@@ -249,7 +246,7 @@ class FingerprintManagerInteractorImpl(
cancellationSignal,
authenticationCallback,
null,
applicationContext.userId
applicationContext.userId,
)
}
}

View File

@@ -14,5 +14,5 @@
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settings.biometrics.fingerprint2.shared">
package="com.android.settings.biometrics.fingerprint2.lib">
</manifest>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* 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.
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.domain.interactor
package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
@@ -56,8 +56,8 @@ interface FingerprintManagerInteractor {
/**
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
* enrollment. Returning the [FingerEnrollState] that represents this fingerprint
* enrollment state.
* enrollment. Returning the [FingerEnrollState] that represents this fingerprint enrollment
* state.
*/
suspend fun enroll(
hardwareAuthToken: ByteArray?,
@@ -78,5 +78,4 @@ interface FingerprintManagerInteractor {
/** Indicates if the press to auth feature has been enabled */
suspend fun pressToAuthEnabled(): Boolean
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.model
package com.android.settings.biometrics.fingerprint2.lib.model
/** The reason for enrollment */
enum class EnrollReason {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.model
package com.android.settings.biometrics.fingerprint2.lib.model
import android.annotation.StringRes
@@ -28,16 +28,12 @@ sealed class FingerEnrollState {
*
* Progress is obtained by (totalStepsRequired - remainingSteps) / totalStepsRequired
*/
data class EnrollProgress(
val remainingSteps: Int,
val totalStepsRequired: Int,
) : FingerEnrollState()
data class EnrollProgress(val remainingSteps: Int, val totalStepsRequired: Int) :
FingerEnrollState()
/** Represents that recoverable error has been encountered during enrollment. */
data class EnrollHelp(
@StringRes val helpMsgId: Int,
val helpString: String,
) : FingerEnrollState()
data class EnrollHelp(@StringRes val helpMsgId: Int, val helpString: String) :
FingerEnrollState()
/** Represents that an unrecoverable error has been encountered and the operation is complete. */
data class EnrollError(

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* 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.
@@ -14,21 +14,13 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.model
data class FingerprintData(
val name: String,
val fingerId: Int,
val deviceId: Long,
)
package com.android.settings.biometrics.fingerprint2.lib.model
/** Information indicating whether an auth was successful or not */
sealed class FingerprintAuthAttemptModel {
data class Success(
val fingerId: Int,
) : FingerprintAuthAttemptModel()
/** Indicates a successful auth attempt has occurred for [fingerId] */
data class Success(val fingerId: Int) : FingerprintAuthAttemptModel()
data class Error(
val error: Int,
val message: String,
) : FingerprintAuthAttemptModel()
/** Indicates a failed auth attempt has occurred. */
data class Error(val error: Int, val message: String) : FingerprintAuthAttemptModel()
}

View File

@@ -14,14 +14,7 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.data.repository
package com.android.settings.biometrics.fingerprint2.lib.model
/**
* Interface that indicates if press to auth is on or off.
*/
interface PressToAuthProvider {
/**
* Indicates true if the PressToAuth feature is enabled, false otherwise.
*/
val isEnabled: Boolean
}
/** Basic information about an enrolled fingerprint */
data class FingerprintData(val name: String, val fingerId: Int, val deviceId: Long)

View File

@@ -14,10 +14,11 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.shared.model
package com.android.settings.biometrics.fingerprint2.lib.model
/**
* The [FingerprintFlow] for fingerprint enrollment indicates information on how the flow should behave.
* The [FingerprintFlow] for fingerprint enrollment indicates information on how the flow should
* behave.
*/
sealed class FingerprintFlow
@@ -32,3 +33,6 @@ data object Unicorn : FingerprintFlow()
/** Flow to specify settings type */
data object Settings : FingerprintFlow()
/** Indicates that the fast enroll experience should occur */
data object FastEnroll : FingerprintFlow()

View File

@@ -21,7 +21,6 @@ import android.content.Intent
import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.accessibility.AccessibilityManager
import androidx.activity.result.contract.ActivityResultContracts
@@ -31,16 +30,18 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.internal.widget.LockPatternUtils
import com.android.settings.R
import com.android.settings.SettingsApplication
import com.android.settings.SetupWizardUtils
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepoImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.shared.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.repository.PressToAuthProviderImpl
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
@@ -49,20 +50,24 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
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.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.ConfirmDeviceCredential
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Confirmation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Init
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep
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.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Intro
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.LaunchConfirmDeviceCredential
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.password.ChooseLockGeneric
import com.android.settings.password.ChooseLockSettingsHelper
@@ -71,7 +76,6 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.google.android.setupcompat.util.WizardManagerHelper
import com.google.android.setupdesign.util.ThemeHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
@@ -83,7 +87,7 @@ private const val TAG = "FingerprintEnrollmentV2Activity"
*/
class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel
private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
private lateinit var accessibilityViewModel: AccessibilityViewModel
@@ -91,6 +95,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var orientationStateViewModel: OrientationStateViewModel
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
private val coroutineDispatcher = Dispatchers.Default
/** Result listener for ChooseLock activity flow. */
@@ -119,6 +124,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
super.onResume()
backgroundViewModel.inForeground()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
foldStateViewModel.onConfigurationChange(newConfig)
@@ -127,8 +133,20 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK
val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long?
lifecycleScope.launch {
val confirmDeviceResult =
if (wasSuccessful) {
FingerprintAction.CONFIRM_DEVICE_SUCCESS
} else {
FingerprintAction.CONFIRM_DEVICE_FAIL
}
gatekeeperViewModel.onConfirmDevice(wasSuccessful, gateKeeperPasswordHandle)
navigationViewModel.update(
confirmDeviceResult,
ConfirmDeviceCredential::class,
"$TAG#onConfirmDevice",
)
}
}
@@ -156,14 +174,21 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
ViewModelProvider(this, BackgroundViewModel.BackgroundViewModelFactory())[
BackgroundViewModel::class.java]
fingerprintFlowViewModel =
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
FingerprintFlowViewModel::class.java]
val interactor =
val fingerprintSensorRepo =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val fingerprintManagerInteractor =
FingerprintManagerInteractorImpl(
context,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorRepo,
GatekeeperPasswordProvider(LockPatternUtils(context)),
PressToAuthProviderImpl(context),
PressToAuthRepoImpl(context),
enrollType,
)
@@ -171,27 +196,37 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
val hasConfirmedDeviceCredential = gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo
navigationViewModel =
ViewModelProvider(
this,
FingerprintNavigationViewModel.FingerprintNavigationViewModelFactory(
Init,
hasConfirmedDeviceCredential,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintNavigationViewModel::class.java]
// Initialize FingerprintEnrollIntroViewModel
ViewModelProvider(
this,
FingerprintEnrollIntroViewModel.FingerprintEnrollIntoViewModelFactory(
navigationViewModel,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintEnrollIntroViewModel::class.java]
gatekeeperViewModel =
ViewModelProvider(
this,
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
gatekeeperInfo,
interactor,
)
fingerprintManagerInteractor,
),
)[FingerprintGatekeeperViewModel::class.java]
navigationViewModel =
ViewModelProvider(
this,
FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
backgroundDispatcher,
interactor,
gatekeeperViewModel,
gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo,
enrollType,
)
)[FingerprintEnrollNavigationViewModel::class.java]
// Initialize FoldStateViewModel
foldStateViewModel =
ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[
@@ -203,10 +238,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
ViewModelProvider(
this,
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
interactor,
fingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
)
),
)[FingerprintEnrollViewModel::class.java]
// Initialize scroll view model
@@ -220,7 +255,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
this,
AccessibilityViewModel.AccessibilityViewModelFactory(
getSystemService(AccessibilityManager::class.java)!!
)
),
)[AccessibilityViewModel::class.java]
// Initialize OrientationViewModel
@@ -234,8 +269,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
this,
FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory(
fingerprintEnrollViewModel,
backgroundViewModel
)
backgroundViewModel,
),
)[FingerprintEnrollEnrollingViewModel::class.java]
// Initialize FingerprintEnrollFindSensorViewModel
@@ -248,36 +283,48 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel
)
orientationStateViewModel,
fingerprintFlowViewModel,
),
)[FingerprintEnrollFindSensorViewModel::class.java]
// Initialize RFPS View Model
ViewModelProvider(
this,
RFPSViewModel.RFPSViewModelFactory(fingerprintEnrollEnrollingViewModel)
RFPSViewModel.RFPSViewModelFactory(fingerprintEnrollEnrollingViewModel, navigationViewModel),
)[RFPSViewModel::class.java]
lifecycleScope.launch {
navigationViewModel.currentStep.collect { step ->
if (step is Init) {
Log.d(TAG, "FingerprintNav.init($step)")
navigationViewModel.update(FingerprintAction.ACTIVITY_CREATED, Init::class, "$TAG#init")
}
}
}
lifecycleScope.launch {
navigationViewModel.navigationViewModel
.filterNotNull()
.combine(fingerprintEnrollViewModel.sensorType) { nav, sensorType -> Pair(nav, sensorType) }
.collect { (nav, sensorType) ->
Log.d(TAG, "navigationStep $nav")
fingerprintEnrollViewModel.sensorTypeCached = sensorType
val isForward = nav.forward
val currStep = nav.currStep
val theClass: Class<Fragment>? =
when (currStep) {
Confirmation -> FingerprintEnrollConfirmationV2Fragment::class.java as Class<Fragment>
Education -> FingerprintEnrollFindSensorV2Fragment::class.java as Class<Fragment>
navigationViewModel.navigateTo.filterNotNull().collect { step ->
if (step is ConfirmDeviceCredential) {
launchConfirmOrChooseLock(userId)
navigationViewModel.update(
FingerprintAction.TRANSITION_FINISHED,
TransitionStep::class,
"$TAG#launchConfirmOrChooseLock",
)
} else {
val theClass: Fragment? =
when (step) {
Confirmation -> FingerprintEnrollConfirmationV2Fragment()
is Education -> {
FingerprintEnrollFindSensorV2Fragment(step.sensor.sensorType)
}
is Enrollment -> {
when (sensorType) {
FingerprintSensorType.REAR -> RFPSEnrollFragment::class.java as Class<Fragment>
else -> FingerprintEnrollEnrollingV2Fragment::class.java as Class<Fragment>
when (step.sensor.sensorType) {
FingerprintSensorType.REAR -> RFPSEnrollFragment()
else -> FingerprintEnrollEnrollingV2Fragment()
}
}
Intro -> FingerprintEnrollIntroV2Fragment::class.java as Class<Fragment>
Introduction -> FingerprintEnrollIntroV2Fragment()
else -> null
}
@@ -291,18 +338,30 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, theClass, null)
.commit()
} else {
navigationViewModel.update(
FingerprintAction.TRANSITION_FINISHED,
TransitionStep::class,
"$TAG#fragmentManager.add($theClass)",
)
}
}
}
}
if (currStep is Finish) {
if (currStep.resultCode != null) {
finishActivity(currStep.resultCode)
lifecycleScope.launch {
navigationViewModel.shouldFinish.filterNotNull().collect {
Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
if (it.result != null) {
finishActivity(it.result as Int)
} else {
finish()
}
} else if (currStep == LaunchConfirmDeviceCredential) {
launchConfirmOrChooseLock(userId)
}
}
lifecycleScope.launch {
navigationViewModel.currentScreen.filterNotNull().collect { screen ->
Log.d(TAG, "FingerprintSettingsNav.currentScreen($screen)")
}
}
@@ -313,7 +372,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
) {
overridePendingTransition(
com.google.android.setupdesign.R.anim.sud_slide_next_in,
com.google.android.setupdesign.R.anim.sud_slide_next_out
com.google.android.setupdesign.R.anim.sud_slide_next_out,
)
}
}

View File

@@ -18,8 +18,6 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
/**
* A fragment to indicate that fingerprint enrollment has been completed.
@@ -31,9 +29,5 @@ class FingerprintEnrollConfirmationV2Fragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val navigationViewModel =
ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
}
}
}

View File

@@ -18,18 +18,12 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
/** A fragment that is responsible for enrolling a users fingerprint. */
class FingerprintEnrollEnrollingV2Fragment : Fragment(R.layout.fingerprint_enroll_enrolling) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val navigationViewModel =
ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
}
}
}

View File

@@ -50,7 +50,7 @@ private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
* 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
* will work.
*/
class FingerprintEnrollFindSensorV2Fragment : Fragment() {
class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorType) : Fragment() {
// This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
private var animation: FingerprintFindSensorAnimation? = null
@@ -62,7 +62,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View? {
val sensorType =
@@ -104,8 +104,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
}
lifecycleScope.launch {
viewModel.showRfpsAnimation.collect {
animation =
view.findViewById(R.id.fingerprint_sensor_location_animation)
animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
animation!!.startAnimation()
}
}
@@ -128,14 +127,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
footerBarMixin.secondaryButton =
FooterButton.Builder(requireActivity())
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
.setListener {
run {
// TODO: Show the dialog for suw
Log.d(TAG, "onSkipClicked")
// TODO: Finish activity in the root activity instead.
requireActivity().finish()
}
}
.setListener { viewModel.secondaryButtonClicked() }
.setButtonType(FooterButton.ButtonType.SKIP)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
.build()
@@ -146,11 +138,9 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
FooterButton.Builder(requireActivity())
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
.setListener {
run {
Log.d(TAG, "onStartButtonClick")
viewModel.proceedToEnrolling()
}
}
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
.build()
@@ -159,7 +149,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
private fun setupLottie(
view: View,
lottieAnimation: Int,
lottieClickListener: View.OnClickListener? = null
lottieClickListener: View.OnClickListener? = null,
) {
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
illustrationLottie?.setAnimation(lottieAnimation)

View File

@@ -24,7 +24,6 @@ import android.graphics.PorterDuffColorFilter
import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -36,9 +35,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.Unicorn
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.lib.model.Unicorn
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
@@ -48,6 +46,7 @@ import com.google.android.setupdesign.GlifLayout
import com.google.android.setupdesign.template.RequireScrollMixin
import com.google.android.setupdesign.util.DynamicColorPalette
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
private const val TAG = "FingerprintEnrollmentIntroV2Fragment"
@@ -96,16 +95,14 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
private lateinit var footerBarMixin: FooterBarMixin
private lateinit var textModel: TextModel
// Note that the ViewModels cannot be requested before the onCreate call
private val navigationViewModel: FingerprintEnrollNavigationViewModel by lazy {
viewModelProvider[FingerprintEnrollNavigationViewModel::class.java]
}
private val fingerprintViewModel: FingerprintEnrollViewModel by lazy {
viewModelProvider[FingerprintEnrollViewModel::class.java]
private val viewModel: FingerprintEnrollIntroViewModel by lazy {
viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
}
private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy {
viewModelProvider[FingerprintScrollViewModel::class.java]
}
private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy {
viewModelProvider[FingerprintGatekeeperViewModel::class.java]
}
@@ -113,17 +110,16 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View? =
super.onCreateView(inflater, container, savedInstanceState).also { theView ->
val view = theView!!
viewLifecycleOwner.lifecycleScope.launch {
combine(
navigationViewModel.fingerprintFlow,
fingerprintViewModel.sensorType,
) { enrollType, sensorType ->
Pair(enrollType, sensorType)
combine(viewModel.fingerprintFlow, viewModel.sensor.filterNotNull()) {
enrollType,
sensorType ->
Pair(enrollType, sensorType.sensorType)
}
.collect { (enrollType, sensorType) ->
textModel =
@@ -147,7 +143,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
R.id.icon_trash_can,
R.id.icon_info,
R.id.icon_shield,
R.id.icon_link
R.id.icon_link,
)
.forEach { icon ->
view.requireViewById<ImageView>(icon).drawable.colorFilter = colorFilter
@@ -186,31 +182,24 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
return view
}
/**
* TODO (b/305269201): This link isn't displaying for screenshot tests.
*/
/** TODO (b/305269201): This link isn't displaying for screenshot tests. */
private fun setFooterLink(view: View) {
val footerLink: TextView = view.requireViewById(R.id.footer_learn_more)
footerLink.movementMethod = LinkMovementMethod.getInstance()
footerLink.text =
Html.fromHtml(
getString(R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more),
Html.FROM_HTML_MODE_LEGACY
Html.FROM_HTML_MODE_LEGACY,
)
}
private fun setupFooterBarAndScrollView(
view: View,
) {
private fun setupFooterBarAndScrollView(view: View) {
val scrollView: ScrollView =
view.requireViewById(com.google.android.setupdesign.R.id.sud_scroll_view)
scrollView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
// Next button responsible for starting the next fragment.
val onNextButtonClick: View.OnClickListener =
View.OnClickListener {
Log.d(TAG, "OnNextClicked")
navigationViewModel.nextStep()
}
View.OnClickListener { viewModel.primaryButtonClicked() }
val layout: GlifLayout = view.findViewById(R.id.setup_wizard_layout)!!
footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
@@ -225,11 +214,11 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
footerBarMixin.setSecondaryButton(
FooterButton.Builder(requireContext())
.setText(textModel.negativeButton)
.setListener({ Log.d(TAG, "prevClicked") })
.setListener { viewModel.onSecondaryButtonClicked() }
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
.build(),
true /* usePrimaryStyle */
true, /* usePrimaryStyle */
)
val primaryButton = footerBarMixin.primaryButton
@@ -242,7 +231,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
requireContext(),
primaryButton,
R.string.security_settings_face_enroll_introduction_more,
onNextButtonClick
onNextButtonClick,
)
requireScrollMixin.setOnRequireScrollStateChangedListener { scrollNeeded: Boolean ->
@@ -257,7 +246,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
if (consented) {
primaryButton.setText(
requireContext(),
R.string.security_settings_fingerprint_enroll_introduction_agree
R.string.security_settings_fingerprint_enroll_introduction_agree,
)
secondaryButton.visibility = View.VISIBLE
} else {
@@ -309,7 +298,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
private fun getIconColorFilter(): PorterDuffColorFilter {
return PorterDuffColorFilter(
DynamicColorPalette.getColor(context, DynamicColorPalette.ColorType.ACCENT),
PorterDuff.Mode.SRC_IN
PorterDuff.Mode.SRC_IN,
)
}
}

View File

@@ -32,13 +32,15 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.IconTouchDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import com.google.android.setupcompat.template.FooterBarMixin
@@ -49,8 +51,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
private const val TAG = "RFPSEnrollFragment"
/** This fragment is responsible for taking care of rear fingerprint enrollment. */
class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrolling) {
@@ -74,11 +74,14 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
private val backgroundViewModel: BackgroundViewModel by lazy {
ViewModelProvider(requireActivity())[BackgroundViewModel::class.java]
}
private val navigationViewModel: FingerprintNavigationViewModel by lazy {
ViewModelProvider(requireActivity())[FingerprintNavigationViewModel::class.java]
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)!!
val fragment = this
@@ -99,7 +102,7 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
footerBarMixin.secondaryButton =
FooterButton.Builder(context)
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
.setListener { Log.e(TAG, "skip enrollment!") }
.setListener { rfpsViewModel.negativeButtonClicked() }
.setButtonType(FooterButton.ButtonType.SKIP)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
.build()
@@ -150,7 +153,7 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
viewLifecycleOwner.lifecycleScope.launch {
backgroundViewModel.background
.filter { inBackground -> inBackground }
.collect { rfpsViewModel.stopEnrollment() }
.collect { rfpsViewModel.didGoToBackground() }
}
viewLifecycleOwner.lifecycleScope.launch {
@@ -171,7 +174,6 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
.setDuration(200)
.setInterpolator(linearOutSlowInInterpolator)
.start()
}
}
@@ -207,6 +209,12 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
dismissDialogs()
}
}
viewLifecycleOwner.lifecycleScope.launch {
rfpsViewModel.didCompleteEnrollment
.filter { it }
.collect { rfpsViewModel.finishedSuccessfully() }
}
return view
}
@@ -215,28 +223,18 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
viewLifecycleOwner.lifecycleScope.launch {
try {
val shouldRestartEnrollment = FingerprintErrorDialog.showInstance(error, fragment)
rfpsViewModel.userClickedStopEnrollDialog()
} catch (exception: Exception) {
Log.e(TAG, "Exception occurred $exception")
}
onEnrollmentFailed()
}
}
private fun onEnrollmentFailed() {
rfpsViewModel.stopEnrollment()
}
private fun handleEnrollProgress(progress: FingerEnrollState.EnrollProgress) {
progressBar.updateProgress(
progress.remainingSteps.toFloat() / progress.totalStepsRequired.toFloat()
)
if (progress.remainingSteps == 0) {
performNextStepSuccess()
}
}
private fun performNextStepSuccess() {}
private fun dismissDialogs() {
val transaction = parentFragmentManager.beginTransaction()
@@ -249,4 +247,9 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
}
transaction.commitAllowingStateLoss()
}
companion object {
private const val TAG = "RFPSEnrollFragment"
private val navStep = FingerprintNavigationStep.Enrollment::class
}
}

View File

@@ -38,9 +38,9 @@ class RFPSIconTouchViewModel : ViewModel() {
private val _touches: MutableStateFlow<Int> = MutableStateFlow(0)
/**
* Whether or not the UI should be showing the dialog. By making this SharingStarted.Eagerly
* the first event 0 % 3 == 0 will fire as soon as this view model is created, so it should
* be ignored and work as intended.
* Whether or not the UI should be showing the dialog. By making this SharingStarted.Eagerly the
* first event 0 % 3 == 0 will fire as soon as this view model is created, so it should be ignored
* and work as intended.
*/
val shouldShowDialog: Flow<Boolean> =
_touches

View File

@@ -19,13 +19,17 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.transform
@@ -34,9 +38,10 @@ import kotlinx.coroutines.flow.update
/** View Model used by the rear fingerprint enrollment fragment. */
class RFPSViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModel() {
/** Value to indicate if the text view is visible or not **/
/** Value to indicate if the text view is visible or not */
private val _textViewIsVisible = MutableStateFlow<Boolean>(false)
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
@@ -61,9 +66,8 @@ class RFPSViewModel(
val helpMessage: Flow<FingerEnrollState.EnrollHelp?> =
enrollFlow
.filterIsInstance<FingerEnrollState.EnrollHelp>()
.shareIn(viewModelScope, SharingStarted.Eagerly, 0).transform {
_textViewIsVisible.update { true }
}
.shareIn(viewModelScope, SharingStarted.Eagerly, 0)
.transform { _textViewIsVisible.update { true } }
/**
* The error message should only be shown once, for scenarios like screen rotations, we don't want
@@ -74,6 +78,8 @@ class RFPSViewModel(
.filterIsInstance<FingerEnrollState.EnrollError>()
.shareIn(viewModelScope, SharingStarted.Eagerly, 0)
val didCompleteEnrollment: Flow<Boolean> = progress.filterNotNull().map { it.remainingSteps == 0 }
/** Indicates if the consumer is ready for enrollment */
fun readyForEnrollment() {
fingerprintEnrollViewModel.canEnroll()
@@ -88,15 +94,51 @@ class RFPSViewModel(
_textViewIsVisible.update { isVisible }
}
/** Indicates that the user is done with trying to enroll a fingerprint */
fun userClickedStopEnrollDialog() {
navigationViewModel.update(
FingerprintAction.USER_CLICKED_FINISH,
navStep,
"${TAG}#userClickedStopEnrollingDialog",
)
}
/** Indicates that the application went to the background. */
fun didGoToBackground() {
navigationViewModel.update(
FingerprintAction.DID_GO_TO_BACKGROUND,
navStep,
"${TAG}#didGoToBackground",
)
stopEnrollment()
}
/** Indicates the negative button has been clicked */
fun negativeButtonClicked() {
navigationViewModel.update(
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
navStep,
"${TAG}negativeButtonClicked",
)
}
fun finishedSuccessfully() {
navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished")
}
class RFPSViewModelFactory(
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
return RFPSViewModel(fingerprintEnrollEnrollingViewModel) as T
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel) as T
}
}
companion object {
private val navStep = Enrollment::class
private const val TAG = "RFPSViewModel"
}
}

View File

@@ -24,7 +24,7 @@ import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -86,10 +86,8 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
private const val KEY_TITLE = "fingerprint_title"
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
suspend fun showInstance(
error: FingerEnrollState.EnrollError,
fragment: Fragment,
) = suspendCancellableCoroutine { continuation ->
suspend fun showInstance(error: FingerEnrollState.EnrollError, fragment: Fragment) =
suspendCancellableCoroutine { continuation ->
val dialog = FingerprintErrorDialog()
dialog.onTryAgain = DialogInterface.OnClickListener { _, _ -> continuation.resume(true) }
@@ -104,18 +102,9 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
continuation.invokeOnCancellation { Log.d(TAG, "invokeOnCancellation $dialog") }
val bundle = Bundle()
bundle.putInt(
KEY_TITLE,
error.errTitle,
)
bundle.putInt(
KEY_MESSAGE,
error.errString,
)
bundle.putBoolean(
KEY_SHOULD_TRY_AGAIN,
error.shouldRetryEnrollment,
)
bundle.putInt(KEY_TITLE, error.errTitle)
bundle.putInt(KEY_MESSAGE, error.errString)
bundle.putBoolean(KEY_SHOULD_TRY_AGAIN, error.shouldRetryEnrollment)
dialog.arguments = bundle
Log.d(TAG, "showing dialog $dialog")
dialog.show(fragment.parentFragmentManager, FingerprintErrorDialog::class.java.toString())

View File

@@ -41,15 +41,13 @@ class AccessibilityViewModel(accessibilityManager: AccessibilityManager) : ViewM
.stateIn(
viewModelScope, // This is going to tied to the view model scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
false
false,
)
class AccessibilityViewModelFactory(private val accessibilityManager: AccessibilityManager) :
ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AccessibilityViewModel(accessibilityManager) as T
}
}

View File

@@ -25,8 +25,8 @@ import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
/**
* This class is a wrapper around the [FingerprintEnrollViewModel] and decides when
* the user should or should not be enrolling.
* This class is a wrapper around the [FingerprintEnrollViewModel] and decides when the user should
* or should not be enrolling.
*/
class FingerprintEnrollEnrollingViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
@@ -72,12 +72,10 @@ class FingerprintEnrollEnrollingViewModel(
class FingerprintEnrollEnrollingViewModelFactory(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val backgroundViewModel: BackgroundViewModel
private val backgroundViewModel: BackgroundViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
as T
}

View File

@@ -19,8 +19,10 @@ 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.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.shared.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,20 +38,22 @@ import kotlinx.coroutines.launch
/** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */
class FingerprintEnrollFindSensorViewModel(
private val navigationViewModel: FingerprintEnrollNavigationViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
backgroundViewModel: BackgroundViewModel,
accessibilityViewModel: AccessibilityViewModel,
foldStateViewModel: FoldStateViewModel,
orientationStateViewModel: OrientationStateViewModel
orientationStateViewModel: OrientationStateViewModel,
fingerprintFlowViewModel: FingerprintFlowViewModel,
) : ViewModel() {
/** Represents the stream of sensor type. */
val sensorType: Flow<FingerprintSensorType> =
fingerprintEnrollViewModel.sensorType.shareIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
1
1,
)
private val _isUdfps: Flow<Boolean> =
sensorType.map {
@@ -103,10 +107,10 @@ class FingerprintEnrollFindSensorViewModel(
fingerprintEnrollViewModel.sensorType,
gatekeeperViewModel.hasValidGatekeeperInfo,
gatekeeperViewModel.gatekeeperInfo,
navigationViewModel.navigationViewModel
) { sensorType, hasValidGatekeeperInfo, gatekeeperInfo, navigationViewModel ->
navigationViewModel.currentScreen,
) { sensorType, hasValidGatekeeperInfo, gatekeeperInfo, currStep ->
val shouldStartEnroll =
navigationViewModel.currStep == Education &&
currStep is Education &&
sensorType != FingerprintSensorType.UDFPS_OPTICAL &&
sensorType != FingerprintSensorType.UDFPS_ULTRASONIC &&
hasValidGatekeeperInfo
@@ -128,22 +132,19 @@ class FingerprintEnrollFindSensorViewModel(
// Only collect the flow when we should be running.
if (it) {
combine(
navigationViewModel.fingerprintFlow,
fingerprintEnrollViewModel.educationEnrollFlow.filterNotNull(),
) { enrollType, educationFlow ->
Pair(enrollType, educationFlow)
fingerprintFlowViewModel.fingerprintFlow,
) { educationFlow, type ->
Pair(educationFlow, type)
}
.collect { (enrollType, educationFlow) ->
.collect { (educationFlow, type) ->
when (educationFlow) {
// TODO: Cancel the enroll() when EnrollProgress is received instead of proceeding
// to
// Enrolling page. Otherwise Enrolling page will receive the EnrollError.
is FingerEnrollState.EnrollProgress -> proceedToEnrolling()
is FingerEnrollState.EnrollError -> {
if (educationFlow.isCancelled) {
proceedToEnrolling()
} else {
_showErrorDialog.update { Pair(educationFlow.errString, enrollType == SetupWizard) }
_showErrorDialog.update { Pair(educationFlow.errString, type == SetupWizard) }
}
}
is FingerEnrollState.EnrollHelp -> {}
@@ -169,17 +170,28 @@ class FingerprintEnrollFindSensorViewModel(
/** Proceed to EnrollEnrolling page. */
fun proceedToEnrolling() {
navigationViewModel.nextStep()
stopEducation()
navigationViewModel.update(FingerprintAction.NEXT, navStep, "$TAG#proceedToEnrolling")
}
/** Indicates the secondary button has been clicked */
fun secondaryButtonClicked() {
navigationViewModel.update(
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
navStep,
"${TAG}#secondaryButtonClicked",
)
}
class FingerprintEnrollFindSensorViewModelFactory(
private val navigationViewModel: FingerprintEnrollNavigationViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
private val backgroundViewModel: BackgroundViewModel,
private val accessibilityViewModel: AccessibilityViewModel,
private val foldStateViewModel: FoldStateViewModel,
private val orientationStateViewModel: OrientationStateViewModel
private val orientationStateViewModel: OrientationStateViewModel,
private val fingerprintFlowViewModel: FingerprintFlowViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@@ -190,9 +202,15 @@ class FingerprintEnrollFindSensorViewModel(
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel
orientationStateViewModel,
fingerprintFlowViewModel,
)
as T
}
}
companion object {
private const val TAG = "FingerprintEnrollFindSensorViewModel"
private val navStep = Education::class
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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 com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
/** A view model for fingerprint enroll introduction. */
class FingerprintEnrollIntroViewModel(
val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintFlowViewModel: FingerprintFlowViewModel,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModel() {
/** Represents a stream of [FingerprintSensor] */
val sensor: Flow<FingerprintSensor?> = fingerprintManagerInteractor.sensorPropertiesInternal
/** Represents a stream of [FingerprintFlow] */
val fingerprintFlow: Flow<FingerprintFlow?> = fingerprintFlowViewModel.fingerprintFlow
/** Indicates the primary button has been clicked */
fun primaryButtonClicked() {
navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#onNextClicked")
}
/** Indicates the secondary button has been clicked */
fun onSecondaryButtonClicked() {
navigationViewModel.update(
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
navStep,
"${TAG}#negativeButtonClicked",
)
}
class FingerprintEnrollIntoViewModelFactory(
val navigationViewModel: FingerprintNavigationViewModel,
val fingerprintFlowViewModel: FingerprintFlowViewModel,
val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollIntroViewModel(
navigationViewModel,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
)
as T
}
}
companion object {
val navStep = Introduction::class
private const val TAG = "FingerprintEnrollIntroViewModel"
}
}

View File

@@ -18,9 +18,11 @@ 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.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -34,7 +36,7 @@ import kotlinx.coroutines.flow.transformLatest
class FingerprintEnrollViewModel(
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
gatekeeperViewModel: FingerprintGatekeeperViewModel,
navigationViewModel: FingerprintEnrollNavigationViewModel,
val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModel() {
/**
@@ -45,8 +47,8 @@ class FingerprintEnrollViewModel(
*/
var sensorTypeCached: FingerprintSensorType? = null
private var _enrollReason: Flow<EnrollReason?> =
navigationViewModel.navigationViewModel.map {
when (it.currStep) {
navigationViewModel.currentScreen.map {
when (it) {
is Enrollment -> EnrollReason.EnrollEnrolling
is Education -> EnrollReason.FindSensor
else -> null
@@ -64,8 +66,7 @@ class FingerprintEnrollViewModel(
* This flow should be the only flow which calls enroll().
*/
val _enrollFlow: Flow<FingerEnrollState> =
combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason,
->
combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason ->
Pair(hardwareAuthToken, enrollReason)
}
.transformLatest {
@@ -110,18 +111,11 @@ class FingerprintEnrollViewModel(
class FingerprintEnrollViewModelFactory(
val interactor: FingerprintManagerInteractor,
val gatekeeperViewModel: FingerprintGatekeeperViewModel,
val navigationViewModel: FingerprintEnrollNavigationViewModel,
val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
return FingerprintEnrollViewModel(
interactor,
gatekeeperViewModel,
navigationViewModel,
)
as T
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) as T
}
}
}

View File

@@ -1,141 +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 android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintFlow
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
private const val TAG = "FingerprintEnrollNavigationViewModel"
/**
* This class is responsible for sending a [NavigationStep] which indicates where the user is in the
* Fingerprint Enrollment flow
*/
class FingerprintEnrollNavigationViewModel(
private val dispatcher: CoroutineDispatcher,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
private val firstStep: NextStepViewModel,
private val navState: NavState,
private val theFingerprintFlow: FingerprintFlow,
) : ViewModel() {
private class InternalNavigationStep(
lastStep: NextStepViewModel,
nextStep: NextStepViewModel,
forward: Boolean,
var canNavigate: Boolean,
) : NavigationStep(lastStep, nextStep, forward)
private var _fingerprintFlow = MutableStateFlow<FingerprintFlow?>(theFingerprintFlow)
/** A flow that indicates the [FingerprintFlow] */
val fingerprintFlow: Flow<FingerprintFlow?> = _fingerprintFlow.asStateFlow()
private val _navigationStep =
MutableStateFlow(
InternalNavigationStep(PlaceHolderState, firstStep, forward = false, canNavigate = true)
)
init {
viewModelScope.launch {
gatekeeperViewModel.credentialConfirmed.filterNotNull().collect {
if (_navigationStep.value.currStep is LaunchConfirmDeviceCredential) {
if (it) nextStep() else finish()
}
}
}
}
/**
* A flow that contains the [NavigationStep] used to indicate where in the enrollment process the
* user is.
*/
val navigationViewModel: Flow<NavigationStep> = _navigationStep.asStateFlow()
/** This action indicates that the UI should actually update the navigation to the given step. */
val navigationAction: Flow<NavigationStep?> =
_navigationStep.shareIn(viewModelScope, SharingStarted.Lazily, 0)
/** Used to start the next step of Fingerprint Enrollment. */
fun nextStep() {
viewModelScope.launch {
val currStep = _navigationStep.value.currStep
val nextStep = currStep.next(navState)
Log.d(TAG, "nextStep(${currStep} -> $nextStep)")
_navigationStep.update {
InternalNavigationStep(currStep, nextStep, forward = true, canNavigate = false)
}
}
}
/** Go back a step of fingerprint enrollment. */
fun prevStep() {
viewModelScope.launch {
val currStep = _navigationStep.value.currStep
val nextStep = currStep.prev(navState)
_navigationStep.update {
InternalNavigationStep(currStep, nextStep, forward = false, canNavigate = false)
}
}
}
private fun finish() {
_navigationStep.update {
InternalNavigationStep(Finish(null), Finish(null), forward = false, canNavigate = false)
}
}
class FingerprintEnrollNavigationViewModelFactory(
private val backgroundDispatcher: CoroutineDispatcher,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val fingerprintGatekeeperViewModel: FingerprintGatekeeperViewModel,
private val canSkipConfirm: Boolean,
private val fingerprintFlow: FingerprintFlow,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
val navState = NavState(canSkipConfirm)
return FingerprintEnrollNavigationViewModel(
backgroundDispatcher,
fingerprintManagerInteractor,
fingerprintGatekeeperViewModel,
Start.next(navState),
navState,
fingerprintFlow,
)
as T
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.shareIn
class FingerprintFlowViewModel(private val fingerprintFlowType: FingerprintFlow) : ViewModel() {
val fingerprintFlow: Flow<FingerprintFlow> =
flowOf(fingerprintFlowType).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
class FingerprintFlowViewModelFactory(val flowType: FingerprintFlow) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintFlowViewModel(flowType) as T
}
}
}

View File

@@ -21,7 +21,7 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -29,11 +29,11 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
private const val TAG = "FingerprintGatekeeperViewModel"
sealed interface GatekeeperInfo {
object Invalid : GatekeeperInfo
object Timeout : GatekeeperInfo
data class GatekeeperPasswordInfo(val token: ByteArray?, val passwordHandle: Long?) :
GatekeeperInfo
}
@@ -97,7 +97,19 @@ class FingerprintGatekeeperViewModel(
}
}
class FingerprintGatekeeperViewModelFactory(
private val gatekeeperInfo: GatekeeperInfo?,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintGatekeeperViewModel(gatekeeperInfo, fingerprintManagerInteractor) as T
}
}
companion object {
private const val TAG = "FingerprintGatekeeperViewModel"
/**
* A function that checks if the challenge and token are valid, in which case a
* [GatekeeperInfo.GatekeeperPasswordInfo] is provided, else [GatekeeperInfo.Invalid]
@@ -110,17 +122,4 @@ class FingerprintGatekeeperViewModel(
return GatekeeperInfo.GatekeeperPasswordInfo(token, challenge)
}
}
class FingerprintGatekeeperViewModelFactory(
private val gatekeeperInfo: GatekeeperInfo?,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
return FingerprintGatekeeperViewModel(gatekeeperInfo, fingerprintManagerInteractor) as T
}
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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 com.android.settings.biometrics.fingerprint2.lib.model.FastEnroll
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.systemui.biometrics.shared.model.FingerprintSensor
/**
* A [FingerprintAction] event notifies the current [FingerprintNavigationStep] that an event
* occurred. Depending on the type of [FingerprintAction] and the current
* [FingerprintNavigationStep], the navstep will potentially produce a new
* [FingerprintNavigationStep] indicating either 1). Control flow has changed 2). The activity has
* finished 3). A transition is required
*/
enum class FingerprintAction {
NEXT,
PREV,
CONFIRM_DEVICE_SUCCESS,
CONFIRM_DEVICE_FAIL,
TRANSITION_FINISHED,
DID_GO_TO_BACKGROUND,
ACTIVITY_CREATED,
NEGATIVE_BUTTON_PRESSED,
USER_CLICKED_FINISH,
}
/** State that can be used to help a [FingerprintNavigationStep] determine the next step to take. */
data class NavigationState(
val flowType: FingerprintFlow,
val hasConfirmedDeviceCredential: Boolean,
val fingerprintSensor: FingerprintSensor?,
)
/**
* A generic interface for operating on (state, action) -> state? which will produce either another
* FingerprintNavStep if something is required, or nothing.
*
* Note during the lifetime of the Activity, their should only be one [FingerprintNavigationStep] at
* a time.
*/
sealed interface FingerprintNavigationStep {
fun update(state: NavigationState, action: FingerprintAction): FingerprintNavigationStep?
/**
* This indicates that a transition should occur from one screen to another. This class should
* contain all necessary info about the transition.
*
* A transition step will cause a screen to change ownership from the current screen to the
* [nextUiStep], after the transition has been completed and a
* [FingerprintAction.TRANSITION_FINISHED] has been sent, the [nextUiStep] will be given control.
*/
class TransitionStep(val nextUiStep: UiStep) : FingerprintNavigationStep {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.TRANSITION_FINISHED -> nextUiStep
else -> null
}
}
override fun toString(): String = "TransitionStep(nextUiStep=$nextUiStep)"
}
/** Indicates we should finish the enrolling activity */
data class Finish(val result: Int?) : FingerprintNavigationStep {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? = null
}
/** UiSteps should have a 1 to 1 mapping between each screen of FingerprintEnrollment */
sealed class UiStep : FingerprintNavigationStep
/** This is the landing page for enrollment, where no content is shown. */
data object Init : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.ACTIVITY_CREATED -> {
if (!state.hasConfirmedDeviceCredential) {
TransitionStep(ConfirmDeviceCredential)
} else if (state.flowType is FastEnroll) {
TransitionStep(Enrollment(state.fingerprintSensor!!))
} else {
TransitionStep(Introduction)
}
}
else -> null
}
}
}
/** Indicates the ConfirmDeviceCredential activity is being presented to the user */
data object ConfirmDeviceCredential : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.CONFIRM_DEVICE_SUCCESS -> TransitionStep(Introduction)
FingerprintAction.CONFIRM_DEVICE_FAIL -> Finish(null)
else -> null
}
}
}
/** Indicates the FingerprintIntroduction screen is being presented to the user */
data object Introduction : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.NEXT -> TransitionStep(Education(state.fingerprintSensor!!))
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
FingerprintAction.PREV -> Finish(null)
else -> null
}
}
}
/** Indicates the FingerprintEducation screen is being presented to the user */
data class Education(val sensor: FingerprintSensor) : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.NEXT -> TransitionStep(Enrollment(state.fingerprintSensor!!))
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
FingerprintAction.PREV -> TransitionStep(Introduction)
else -> null
}
}
}
/** Indicates the Enrollment screen is being presented to the user */
data class Enrollment(val sensor: FingerprintSensor) : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.NEXT -> TransitionStep(Confirmation)
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
FingerprintAction.USER_CLICKED_FINISH,
FingerprintAction.DID_GO_TO_BACKGROUND -> Finish(null)
else -> null
}
}
}
/** Indicates the Confirmation screen is being presented to the user */
data object Confirmation : UiStep() {
override fun update(
state: NavigationState,
action: FingerprintAction,
): FingerprintNavigationStep? {
return when (action) {
FingerprintAction.NEXT -> Finish(null)
FingerprintAction.PREV -> TransitionStep(Education(state.fingerprintSensor!!))
else -> null
}
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.UiStep
import java.lang.NullPointerException
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
/**
* This class is essentially a wrapper around [FingerprintNavigationStep] that will be used by
* fragments/viewmodels that want to consume these events. It should provide no additional
* functionality beyond what is available in [FingerprintNavigationStep].
*/
class FingerprintNavigationViewModel(
step: UiStep,
hasConfirmedDeviceCredential: Boolean,
flowViewModel: FingerprintFlowViewModel,
fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModel() {
private var _navStateInternal: MutableStateFlow<NavigationState?> = MutableStateFlow(null)
init {
viewModelScope.launch {
flowViewModel.fingerprintFlow
.combineTransform(fingerprintManagerInteractor.sensorPropertiesInternal) { flow, props ->
if (props?.sensorId != -1) {
emit(NavigationState(flow, hasConfirmedDeviceCredential, props))
}
}
.collect { navState -> _navStateInternal.update { navState } }
}
}
private var _currentStep = MutableStateFlow<FingerprintNavigationStep?>(step)
private var _navigateTo: MutableStateFlow<UiStep?> = MutableStateFlow(null)
val navigateTo: Flow<UiStep?> = _navigateTo.asStateFlow()
/**
* This indicates a navigation event should occur. Navigation depends on navStateInternal being
* present.
*/
val currentStep: Flow<FingerprintNavigationStep> =
_currentStep.filterNotNull().combineTransform(_navStateInternal.filterNotNull()) { navigation, _
->
emit(navigation)
}
private var _finishState = MutableStateFlow<Finish?>(null)
/** This indicates the activity should finish. */
val shouldFinish: Flow<Finish?> = _finishState.asStateFlow()
private var _currentScreen = MutableStateFlow<UiStep?>(null)
/** This indicates what screen should currently be presenting to the user. */
val currentScreen: Flow<UiStep?> = _currentScreen.asStateFlow()
/** See [updateInternal] for more details */
fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) {
Log.d(TAG, "$caller.update($action) $debugStr")
val currentStep = _currentStep.value
val isUiStep = currentStep is UiStep
if (currentStep == null) {
throw NullPointerException("current step is null")
}
if (isUiStep && currentStep::class != caller) {
throw IllegalAccessError(
"Error $currentStep != $caller, $caller should not be sending any events at this time"
)
}
val navState = _navStateInternal.value
if (navState == null) {
throw NullPointerException("nav state is null")
}
val nextStep = currentStep.update(navState, action) ?: return
Log.d(TAG, "nextStep=$nextStep")
// Whenever an state update occurs, everything should be cleared.
_currentStep.update { nextStep }
_finishState.update { null }
_currentScreen.update { null }
when (nextStep) {
is TransitionStep -> {
_navigateTo.update { nextStep.nextUiStep }
}
is Finish -> {
_finishState.update { nextStep }
}
is UiStep -> {
_currentScreen.update { nextStep }
}
}
}
class FingerprintNavigationViewModelFactory(
private val step: UiStep,
private val hasConfirmedDeviceCredential: Boolean,
private val flowViewModel: FingerprintFlowViewModel,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintNavigationViewModel(
step,
hasConfirmedDeviceCredential,
flowViewModel,
fingerprintManagerInteractor,
)
as T
}
}
companion object {
private const val TAG = "FingerprintNavigationViewModel"
}
}

View File

@@ -39,9 +39,7 @@ class FingerprintScrollViewModel : ViewModel() {
class FingerprintScrollViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintScrollViewModel() as T
}
}

View File

@@ -49,9 +49,7 @@ class FoldStateViewModel(context: Context) : ViewModel() {
class FoldStateViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FoldStateViewModel(context) as T
}
}

View File

@@ -1,104 +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
/**
* A class that represents an action that the consumer should transition between lastStep and
* currStep and in what direction this transition is occurring (e.g. forward or backwards)
*/
open class NavigationStep(
val lastStep: NextStepViewModel,
val currStep: NextStepViewModel,
val forward: Boolean
) {
override fun toString(): String {
return "lastStep=$lastStep, currStep=$currStep, forward=$forward"
}
}
/** The navigation state used by a [NavStep] to determine what the [NextStepViewModel] should be. */
class NavState(val confirmedDevice: Boolean)
interface NavStep<T> {
fun next(state: NavState): T
fun prev(state: NavState): T
}
/**
* A class to represent a high level step (I.E. EnrollmentIntroduction) for FingerprintEnrollment.
*/
sealed class NextStepViewModel : NavStep<NextStepViewModel>
/**
* This is the initial state for the previous step, used to indicate that there have been no
* previous states.
*/
object PlaceHolderState : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Finish(null)
override fun prev(state: NavState): NextStepViewModel = Finish(null)
}
/**
* This state is the initial state for the current step, and will be used to determine if the user
* needs to [LaunchConfirmDeviceCredential] if not, it will go to [Intro]
*/
data object Start : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel =
if (state.confirmedDevice) Intro else LaunchConfirmDeviceCredential
override fun prev(state: NavState): NextStepViewModel = Finish(null)
}
/** State indicating enrollment has been completed */
class Finish(val resultCode: Int?) : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Finish(resultCode)
override fun prev(state: NavState): NextStepViewModel = Finish(null)
}
/** State for the FingerprintEnrollment introduction */
data object Intro : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Education
override fun prev(state: NavState): NextStepViewModel = Finish(null)
}
/** State for the FingerprintEnrollment education */
data object Education : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Enrollment
override fun prev(state: NavState): NextStepViewModel = Intro
}
/** State for the FingerprintEnrollment enrollment */
data object Enrollment : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Confirmation
override fun prev(state: NavState): NextStepViewModel = Education
}
/** State for the FingerprintEnrollment confirmation */
object Confirmation : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Finish(0)
override fun prev(state: NavState): NextStepViewModel = Intro
}
/**
* State used to send the user to the ConfirmDeviceCredential activity. This activity can either
* confirm a users device credential, or have them create one.
*/
object LaunchConfirmDeviceCredential : NextStepViewModel() {
override fun next(state: NavState): NextStepViewModel = Intro
override fun prev(state: NavState): NextStepViewModel = Finish(0)
}

View File

@@ -58,7 +58,7 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
.stateIn(
viewModelScope, // This is going to tied to the view model scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
context.display!!.rotation
context.display!!.rotation,
)
fun getRotationFromDefault(rotation: Int): Int {
@@ -73,9 +73,7 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
class OrientationViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return OrientationStateViewModel(context) as T
}
}

View File

@@ -19,8 +19,8 @@ package com.android.settings.biometrics.fingerprint2.ui.settings.binder
import android.hardware.fingerprint.FingerprintManager
import android.util.Log
import androidx.lifecycle.LifecycleCoroutineScope
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder.FingerprintView
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollAdditionalFingerprint
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollFirstFingerprint
@@ -52,7 +52,7 @@ object FingerprintSettingsViewBinder {
userId: Int,
gateKeeperPasswordHandle: Long?,
challenge: Long?,
challengeToken: ByteArray?
challengeToken: ByteArray?,
)
/** Helper to launch an add fingerprint request */
fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?)
@@ -158,7 +158,7 @@ object FingerprintSettingsViewBinder {
nextStep.userId,
nextStep.gateKeeperPasswordHandle,
nextStep.challenge,
nextStep.challengeToken
nextStep.challengeToken,
)
is EnrollAdditionalFingerprint ->
view.launchAddFingerprint(nextStep.userId, nextStep.challengeToken)

View File

@@ -26,7 +26,7 @@ import android.os.Bundle
import android.os.UserManager
import androidx.appcompat.app.AlertDialog
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -75,8 +75,7 @@ class FingerprintDeletionDialog : InstrumentedDialogFragment() {
message =
devicePolicyManager?.resources?.getString(messageId) {
message + "\n\n" + context.getString(defaultMessageId)
}
?: ""
} ?: ""
}
alertDialog =
@@ -85,7 +84,7 @@ class FingerprintDeletionDialog : InstrumentedDialogFragment() {
.setMessage(message)
.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_delete,
onClickListener
onClickListener,
)
.setNegativeButton(R.string.cancel, onNegativeClickListener)
.create()
@@ -94,6 +93,7 @@ class FingerprintDeletionDialog : InstrumentedDialogFragment() {
companion object {
private const val KEY_FINGERPRINT = "fingerprint"
suspend fun showInstance(
fp: FingerprintData,
lastFingerprint: Boolean,
@@ -109,7 +109,7 @@ class FingerprintDeletionDialog : InstrumentedDialogFragment() {
val bundle = Bundle()
bundle.putObject(
KEY_FINGERPRINT,
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId)
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
)
bundle.putBoolean(KEY_IS_LAST_FINGERPRINT, lastFingerprint)
dialog.arguments = bundle

View File

@@ -22,7 +22,7 @@ import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceViewHolder
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settingslib.widget.TwoTargetPreference
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -33,7 +33,7 @@ class FingerprintSettingsPreference(
context: Context,
val fingerprintViewModel: FingerprintData,
val fragment: FingerprintSettingsV2Fragment,
val isLastFingerprint: Boolean
val isLastFingerprint: Boolean,
) : TwoTargetPreference(context) {
private lateinit var myView: View

View File

@@ -27,7 +27,7 @@ import android.util.Log
import android.widget.ImeAwareEditText
import androidx.appcompat.app.AlertDialog
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -78,7 +78,7 @@ class FingerprintSettingsRenameDialog : InstrumentedDialogFragment() {
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
dend: Int,
): CharSequence? {
for (index in start until end) {
val c = source[index]
@@ -133,13 +133,13 @@ class FingerprintSettingsRenameDialog : InstrumentedDialogFragment() {
val bundle = Bundle()
bundle.putObject(
KEY_FINGERPRINT,
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId)
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
)
dialog.arguments = bundle
Log.d(TAG, "showing dialog $dialog")
dialog.show(
target.parentFragmentManager,
FingerprintSettingsRenameDialog::class.java.toString()
FingerprintSettingsRenameDialog::class.java.toString(),
)
}
}

View File

@@ -45,11 +45,12 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepoImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.repository.PressToAuthProviderImpl
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.shared.model.Settings
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
@@ -128,12 +129,12 @@ class FingerprintSettingsV2Fragment :
if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
navigationViewModel.onEnrollFirstFailure(
"Received RESULT_TIMEOUT when enrolling",
resultCode
resultCode,
)
} else {
navigationViewModel.onEnrollFirstFailure(
"Incorrect resultCode or data was null",
resultCode
resultCode,
)
}
} else {
@@ -212,21 +213,24 @@ class FingerprintSettingsV2Fragment :
context.contentResolver,
Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn,
userHandle
userHandle,
)
}
toReturn == 1
}
val fingerprintSensorProvider =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val interactor =
FingerprintManagerInteractorImpl(
context.applicationContext,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorProvider,
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
PressToAuthProviderImpl(context),
isAnySuw
PressToAuthRepoImpl(context),
Settings,
)
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
@@ -240,8 +244,8 @@ class FingerprintSettingsV2Fragment :
interactor,
backgroundDispatcher,
token,
challenge
)
challenge,
),
)[FingerprintSettingsNavigationViewModel::class.java]
settingsViewModel =
@@ -252,15 +256,10 @@ class FingerprintSettingsV2Fragment :
interactor,
backgroundDispatcher,
navigationViewModel,
)
),
)[FingerprintSettingsViewModel::class.java]
FingerprintSettingsViewBinder.bind(
this,
settingsViewModel,
navigationViewModel,
lifecycleScope,
)
FingerprintSettingsViewBinder.bind(this, settingsViewModel, navigationViewModel, lifecycleScope)
}
override fun getMetricsCategory(): Int {
@@ -364,7 +363,7 @@ class FingerprintSettingsV2Fragment :
RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
activity,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT,
requireActivity().userId
requireActivity().userId,
)
val activity = requireActivity()
val helpIntent =
@@ -404,7 +403,7 @@ class FingerprintSettingsV2Fragment :
column.title =
getString(
R.string.security_settings_fingerprint_enroll_introduction_v3_message,
DeviceHelper.getDeviceName(requireActivity())
DeviceHelper.getDeviceName(requireActivity()),
)
column.learnMoreOnClickListener = learnMoreClickListener
column.learnMoreOverrideText =
@@ -437,13 +436,12 @@ class FingerprintSettingsV2Fragment :
val willDelete =
fingerprintPreferences()
.first { it?.fingerprintViewModel == fingerprintViewModel }
?.askUserToDeleteDialog()
?: false
?.askUserToDeleteDialog() ?: false
if (willDelete) {
mMetricsFeatureProvider.action(
context,
SettingsEnums.ACTION_FINGERPRINT_DELETE,
fingerprintViewModel.fingerId
fingerprintViewModel.fingerId,
)
}
return willDelete
@@ -466,7 +464,7 @@ class FingerprintSettingsV2Fragment :
mMetricsFeatureProvider.action(
context,
SettingsEnums.ACTION_FINGERPRINT_RENAME,
toReturn.first.fingerId
toReturn.first.fingerId,
)
}
return toReturn
@@ -518,12 +516,12 @@ class FingerprintSettingsV2Fragment :
val intent = Intent()
intent.setClassName(
SETTINGS_PACKAGE_NAME,
FingerprintEnrollIntroductionInternal::class.java.name
FingerprintEnrollIntroductionInternal::class.java.name,
)
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
intent.putExtra(
SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE,
)
intent.putExtra(Intent.EXTRA_USER_ID, userId)
@@ -546,7 +544,7 @@ class FingerprintSettingsV2Fragment :
val intent = Intent()
intent.setClassName(
SETTINGS_PACKAGE_NAME,
FingerprintEnrollEnrolling::class.qualifiedName.toString()
FingerprintEnrollEnrolling::class.qualifiedName.toString(),
)
intent.putExtra(Intent.EXTRA_USER_ID, userId)
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, challengeToken)
@@ -568,8 +566,7 @@ class FingerprintSettingsV2Fragment :
return category?.let { cat ->
cat.childrenToList().map { it as FingerprintSettingsPreference? }
}
?: emptyList()
} ?: emptyList()
}
private fun PreferenceCategory.childrenToList(): List<Preference> {

View File

@@ -21,7 +21,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -181,9 +181,7 @@ class FingerprintSettingsNavigationViewModel(
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintSettingsNavigationViewModel(
userId,

View File

@@ -21,9 +21,9 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -66,7 +66,7 @@ class FingerprintSettingsViewModel(
emit(
Pair(
fingerprintManagerInteractor.canEnrollFingerprints.first(),
fingerprintManagerInteractor.maxEnrollableFingerprints.first()
fingerprintManagerInteractor.maxEnrollableFingerprints.first(),
)
)
}
@@ -120,7 +120,7 @@ class FingerprintSettingsViewModel(
_isLockedOut,
_attemptsSoFar,
_fingerprintSensorType,
_sensorNullOrEmpty
_sensorNullOrEmpty,
) {
dialogShowing,
step,
@@ -140,7 +140,7 @@ class FingerprintSettingsViewModel(
"lockedOut=${isLockedOut}," +
"attempts=${attempts}," +
"sensorType=${sensorType}" +
"sensorNullOrEmpty=${sensorNullOrEmpty}"
"sensorNullOrEmpty=${sensorNullOrEmpty}",
)
}
if (sensorNullOrEmpty) {
@@ -294,9 +294,7 @@ class FingerprintSettingsViewModel(
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintSettingsViewModel(
userId,
@@ -318,7 +316,7 @@ private inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(
flow6: Flow<T6>,
flow7: Flow<T7>,
flow8: Flow<T8>,
crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R
crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R,
): Flow<R> {
return combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) { args: Array<*> ->
@Suppress("UNCHECKED_CAST")

View File

@@ -29,10 +29,8 @@ data class EnrollFirstFingerprint(
val challengeToken: ByteArray?,
) : NextStepViewModel()
data class EnrollAdditionalFingerprint(
val userId: Int,
val challengeToken: ByteArray?,
) : NextStepViewModel()
data class EnrollAdditionalFingerprint(val userId: Int, val challengeToken: ByteArray?) :
NextStepViewModel()
data class FinishSettings(val reason: String) : NextStepViewModel()

View File

@@ -16,15 +16,11 @@
package com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
/** Classed use to represent a Dialogs state. */
sealed class PreferenceViewModel {
data class RenameDialog(
val fingerprintViewModel: FingerprintData,
) : PreferenceViewModel()
data class RenameDialog(val fingerprintViewModel: FingerprintData) : PreferenceViewModel()
data class DeleteDialog(
val fingerprintViewModel: FingerprintData,
) : PreferenceViewModel()
data class DeleteDialog(val fingerprintViewModel: FingerprintData) : PreferenceViewModel()
}

View File

@@ -33,16 +33,20 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.runner.AndroidJUnit4
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavigationState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
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.Intro
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavState
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.google.android.setupdesign.GlifLayout
import com.google.android.setupdesign.template.RequireScrollMixin
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -62,18 +66,27 @@ class FingerprintEnrollIntroFragmentTest {
)
private val backgroundDispatcher = StandardTestDispatcher()
private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
private val fingerprintSensor =
FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
var enrollFlow = Default
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
private val navigationViewModel =
FingerprintEnrollNavigationViewModel(
backgroundDispatcher,
interactor,
gatekeeperViewModel,
Intro,
NavState(true),
Default,
FingerprintNavigationViewModel(
Introduction,
false,
flowViewModel,
interactor
)
private var fingerprintViewModel =
FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel)
FingerprintEnrollIntroViewModel(
navigationViewModel,
flowViewModel,
interactor,
)
private var fingerprintScrollViewModel = FingerprintScrollViewModel()
@Before
@@ -85,9 +98,9 @@ class FingerprintEnrollIntroFragmentTest {
modelClass: Class<T>,
): T {
return when (modelClass) {
FingerprintEnrollViewModel::class.java -> fingerprintViewModel
FingerprintEnrollIntroViewModel::class.java -> fingerprintViewModel
FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel
FingerprintNavigationViewModel::class.java -> navigationViewModel
FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
else -> null
}

View File

@@ -30,16 +30,19 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
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.NavState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Start
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import kotlinx.coroutines.test.StandardTestDispatcher
import org.junit.Before
import org.junit.Rule
@@ -60,7 +63,7 @@ class BasicScreenshotTest {
InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getFilesDir()
.getAbsolutePath() + "/settings_screenshots"
.getAbsolutePath() + "/settings_screenshots",
)
)
@@ -70,24 +73,27 @@ class BasicScreenshotTest {
private val gatekeeperViewModel =
FingerprintGatekeeperViewModel(
GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
interactor
interactor,
)
private val backgroundDispatcher = StandardTestDispatcher()
private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
val navState = NavState(true)
private val fingerprintSensor =
FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
private val navigationViewModel = FingerprintEnrollNavigationViewModel(
backgroundDispatcher,
var enrollFlow = Default
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
private val navigationViewModel =
FingerprintNavigationViewModel(
FingerprintNavigationStep.Introduction,
false,
flowViewModel,
interactor,
gatekeeperViewModel,
Start.next(navState),
navState,
Default,
)
private var fingerprintViewModel = FingerprintEnrollViewModel(
interactor, gatekeeperViewModel, navigationViewModel,
)
private var fingerprintViewModel =
FingerprintEnrollIntroViewModel(navigationViewModel, flowViewModel, interactor)
private var fingerprintScrollViewModel = FingerprintScrollViewModel()
@Before
@@ -95,13 +101,11 @@ class BasicScreenshotTest {
val factory =
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when (modelClass) {
FingerprintEnrollViewModel::class.java -> fingerprintViewModel
FingerprintEnrollIntroViewModel::class.java -> fingerprintViewModel
FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel
FingerprintNavigationViewModel::class.java -> navigationViewModel
FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
else -> null
}
@@ -118,11 +122,7 @@ class BasicScreenshotTest {
/** Renders a [view] into a [Bitmap]. */
private fun viewToBitmap(view: View): Bitmap {
val bitmap =
Bitmap.createBitmap(
view.measuredWidth,
view.measuredHeight,
Bitmap.Config.ARGB_8888,
)
Bitmap.createBitmap(view.measuredWidth, view.measuredHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
@@ -136,12 +136,7 @@ class BasicScreenshotTest {
}
fragmentScenario.onFragment { fragment ->
val view = fragment.requireView().findViewById<View>(R.id.enroll_intro_content_view)!!
rule.assertBitmapAgainstGolden(
viewToBitmap(view),
"fp_enroll_intro",
MSSIMMatcher()
)
rule.assertBitmapAgainstGolden(viewToBitmap(view), "fp_enroll_intro", MSSIMMatcher())
}
}
}

View File

@@ -16,11 +16,11 @@
package com.android.settings.testutils2
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength

View File

@@ -26,17 +26,23 @@ import android.os.CancellationSignal
import android.os.Handler
import androidx.test.core.app.ApplicationProvider
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepo
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.shared.data.repository.PressToAuthProvider
import com.android.settings.biometrics.fingerprint2.shared.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepo
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.password.ChooseLockSettingsHelper
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -71,21 +77,28 @@ class FingerprintManagerInteractorTest {
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
private var testScope = TestScope(backgroundDispatcher)
private var pressToAuthProvider =
object : PressToAuthProvider {
private var pressToAuthRepo =
object : PressToAuthRepo {
override val isEnabled: Boolean
get() = false
}
@Before
fun setup() {
val sensor = FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
val fingerprintSensorRepo =
object : FingerprintSensorRepo {
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensor)
}
underTest =
FingerprintManagerInteractorImpl(
context,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorRepo,
gateKeeperPasswordProvider,
pressToAuthProvider,
pressToAuthRepo,
Default,
)
}

View File

@@ -21,16 +21,17 @@ import android.content.res.Configuration
import android.view.accessibility.AccessibilityManager
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NextStepViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavigationState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
@@ -38,7 +39,6 @@ import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -67,7 +67,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
private lateinit var fakeFingerprintManagerInteractor: FakeFingerprintManagerInteractor
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var enrollViewModel: FingerprintEnrollViewModel
private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var accessibilityViewModel: AccessibilityViewModel
private lateinit var foldStateViewModel: FoldStateViewModel
private lateinit var orientationStateViewModel: OrientationStateViewModel
@@ -87,18 +87,19 @@ class FingerprintEnrollFindSensorViewModelV2Test {
gatekeeperViewModel =
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
null,
fakeFingerprintManagerInteractor
fakeFingerprintManagerInteractor,
)
.create(FingerprintGatekeeperViewModel::class.java)
val sensor = FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
navigationViewModel =
FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
backgroundDispatcher,
FingerprintNavigationViewModel(
Education(sensor),
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
gatekeeperViewModel,
canSkipConfirm = true,
Default,
)
.create(FingerprintEnrollNavigationViewModel::class.java)
backgroundViewModel =
BackgroundViewModel.BackgroundViewModelFactory().create(BackgroundViewModel::class.java)
@@ -126,12 +127,10 @@ class FingerprintEnrollFindSensorViewModelV2Test {
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel
orientationStateViewModel,
fingerprintFlowViewModel,
)
.create(FingerprintEnrollFindSensorViewModel::class.java)
// Navigate to Education page
navigationViewModel.nextStep()
}
@After
@@ -141,18 +140,6 @@ class FingerprintEnrollFindSensorViewModelV2Test {
// TODO(b/305094585): test enroll() logic
@Test
fun currentStepIsEducation() =
testScope.runTest {
var step: NextStepViewModel? = null
val job = launch {
navigationViewModel.navigationViewModel.collectLatest { step = it.currStep }
}
advanceUntilIdle()
assertThat(step).isEqualTo(Education)
job.cancel()
}
@Test
fun udfpsLottieInfo() =
testScope.runTest {
@@ -161,7 +148,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
0 /* sensorId */,
SensorStrength.STRONG,
5,
FingerprintSensorType.UDFPS_OPTICAL
FingerprintSensorType.UDFPS_OPTICAL,
)
var udfpsLottieInfo: Boolean? = null
@@ -234,7 +221,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
0 /* sensorId */,
SensorStrength.STRONG,
5,
FingerprintSensorType.UDFPS_OPTICAL
FingerprintSensorType.UDFPS_OPTICAL,
)
var showPrimaryButton: Boolean? = null

View File

@@ -17,16 +17,20 @@
package com.android.settings.fingerprint2.ui.enrollment.viewmodel
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.fingerprint2.shared.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavigationState
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -54,7 +58,7 @@ class FingerprintEnrollEnrollingViewModelTest {
private lateinit var enrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel
private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private val defaultGatekeeperInfo = GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 3), 3)
private var testScope = TestScope(backgroundDispatcher)
@@ -65,18 +69,18 @@ class FingerprintEnrollEnrollingViewModelTest {
gateKeeperViewModel =
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
gatekeeperInfo,
fakeFingerprintManagerInteractor
fakeFingerprintManagerInteractor,
)
.create(FingerprintGatekeeperViewModel::class.java)
val sensor = FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
navigationViewModel =
FingerprintEnrollNavigationViewModel(
backgroundDispatcher,
FingerprintNavigationViewModel(
Enrollment(sensor),
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
gateKeeperViewModel,
Enrollment,
NavState(true),
Default,
)
backgroundViewModel =

View File

@@ -18,7 +18,7 @@ package com.android.settings.fingerprint2.ui.settings
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollFirstFingerprint
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FinishSettings

View File

@@ -17,8 +17,8 @@
package com.android.settings.fingerprint2.ui.settings
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.PreferenceViewModel