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", "src/**/*.kt",
], ],
exclude_srcs: [ exclude_srcs: [
"src/com/android/settings/biometrics/fingerprint2/shared/**/*.kt", "src/com/android/settings/biometrics/fingerprint2/lib/**/*.kt",
], ],
use_resource_processor: true, use_resource_processor: true,
resource_dirs: [ 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.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
object Util { object Util {
fun EnrollReason.toOriginalReason(): Int { fun EnrollReason.toOriginalReason(): Int {
@@ -71,6 +71,4 @@ object Util {
this == FINGERPRINT_ERROR_CANCELED, 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,13 +14,24 @@
* limitations under the License. * 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.content.Context
import android.provider.Settings 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 override val isEnabled: Boolean
get() { get() {
var toReturn: Int = var toReturn: Int =
@@ -43,7 +54,7 @@ class PressToAuthProviderImpl(val context: Context) : PressToAuthProvider {
context.contentResolver, context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn, toReturn,
context.userId context.userId,
) )
} }
return (toReturn == 1) return (toReturn == 1)

View File

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

View File

@@ -14,5 +14,5 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <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> </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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
* limitations under the License. * 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.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.systemui.biometrics.shared.model.FingerprintSensor import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -56,8 +56,8 @@ interface FingerprintManagerInteractor {
/** /**
* Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this * Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
* enrollment. Returning the [FingerEnrollState] that represents this fingerprint * enrollment. Returning the [FingerEnrollState] that represents this fingerprint enrollment
* enrollment state. * state.
*/ */
suspend fun enroll( suspend fun enroll(
hardwareAuthToken: ByteArray?, hardwareAuthToken: ByteArray?,
@@ -78,5 +78,4 @@ interface FingerprintManagerInteractor {
/** Indicates if the press to auth feature has been enabled */ /** Indicates if the press to auth feature has been enabled */
suspend fun pressToAuthEnabled(): Boolean suspend fun pressToAuthEnabled(): Boolean
} }

View File

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

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.biometrics.fingerprint2.shared.model package com.android.settings.biometrics.fingerprint2.lib.model
import android.annotation.StringRes import android.annotation.StringRes
@@ -28,16 +28,12 @@ sealed class FingerEnrollState {
* *
* Progress is obtained by (totalStepsRequired - remainingSteps) / totalStepsRequired * Progress is obtained by (totalStepsRequired - remainingSteps) / totalStepsRequired
*/ */
data class EnrollProgress( data class EnrollProgress(val remainingSteps: Int, val totalStepsRequired: Int) :
val remainingSteps: Int, FingerEnrollState()
val totalStepsRequired: Int,
) : FingerEnrollState()
/** Represents that recoverable error has been encountered during enrollment. */ /** Represents that recoverable error has been encountered during enrollment. */
data class EnrollHelp( data class EnrollHelp(@StringRes val helpMsgId: Int, val helpString: String) :
@StringRes val helpMsgId: Int, FingerEnrollState()
val helpString: String,
) : FingerEnrollState()
/** Represents that an unrecoverable error has been encountered and the operation is complete. */ /** Represents that an unrecoverable error has been encountered and the operation is complete. */
data class EnrollError( 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,21 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.biometrics.fingerprint2.shared.model package com.android.settings.biometrics.fingerprint2.lib.model
data class FingerprintData(
val name: String,
val fingerId: Int,
val deviceId: Long,
)
/** Information indicating whether an auth was successful or not */
sealed class FingerprintAuthAttemptModel { sealed class FingerprintAuthAttemptModel {
data class Success( /** Indicates a successful auth attempt has occurred for [fingerId] */
val fingerId: Int, data class Success(val fingerId: Int) : FingerprintAuthAttemptModel()
) : FingerprintAuthAttemptModel()
data class Error( /** Indicates a failed auth attempt has occurred. */
val error: Int, data class Error(val error: Int, val message: String) : FingerprintAuthAttemptModel()
val message: String,
) : FingerprintAuthAttemptModel()
} }

View File

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

View File

@@ -14,10 +14,11 @@
* limitations under the License. * 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 sealed class FingerprintFlow
@@ -32,3 +33,6 @@ data object Unicorn : FingerprintFlow()
/** Flow to specify settings type */ /** Flow to specify settings type */
data object Settings : FingerprintFlow() 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.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager
import android.os.Bundle import android.os.Bundle
import android.provider.Settings
import android.util.Log import android.util.Log
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@@ -31,16 +30,18 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils
import com.android.settings.R import com.android.settings.R
import com.android.settings.SettingsApplication
import com.android.settings.SetupWizardUtils import com.android.settings.SetupWizardUtils
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
import com.android.settings.biometrics.BiometricEnrollBase import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider 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.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.shared.model.Default import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.shared.model.SetupWizard import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.repository.PressToAuthProviderImpl
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment 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.FingerprintEnrollEnrollingV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment 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.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.AccessibilityViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel 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.FingerprintAction
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.FingerprintEnrollEnrollingViewModel 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.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.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.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.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.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo 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.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.password.ChooseLockGeneric import com.android.settings.password.ChooseLockGeneric
import com.android.settings.password.ChooseLockSettingsHelper 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.setupcompat.util.WizardManagerHelper
import com.google.android.setupdesign.util.ThemeHelper import com.google.android.setupdesign.util.ThemeHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -83,7 +87,7 @@ private const val TAG = "FingerprintEnrollmentV2Activity"
*/ */
class FingerprintEnrollmentV2Activity : FragmentActivity() { class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel private lateinit var fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel
private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
private lateinit var accessibilityViewModel: AccessibilityViewModel private lateinit var accessibilityViewModel: AccessibilityViewModel
@@ -91,6 +95,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var orientationStateViewModel: OrientationStateViewModel private lateinit var orientationStateViewModel: OrientationStateViewModel
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
private lateinit var backgroundViewModel: BackgroundViewModel private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
private val coroutineDispatcher = Dispatchers.Default private val coroutineDispatcher = Dispatchers.Default
/** Result listener for ChooseLock activity flow. */ /** Result listener for ChooseLock activity flow. */
@@ -119,6 +124,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
super.onResume() super.onResume()
backgroundViewModel.inForeground() backgroundViewModel.inForeground()
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
foldStateViewModel.onConfigurationChange(newConfig) foldStateViewModel.onConfigurationChange(newConfig)
@@ -127,8 +133,20 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private fun onConfirmDevice(resultCode: Int, data: Intent?) { private fun onConfirmDevice(resultCode: Int, data: Intent?) {
val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK
val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long? val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long?
lifecycleScope.launch { lifecycleScope.launch {
val confirmDeviceResult =
if (wasSuccessful) {
FingerprintAction.CONFIRM_DEVICE_SUCCESS
} else {
FingerprintAction.CONFIRM_DEVICE_FAIL
}
gatekeeperViewModel.onConfirmDevice(wasSuccessful, gateKeeperPasswordHandle) gatekeeperViewModel.onConfirmDevice(wasSuccessful, gateKeeperPasswordHandle)
navigationViewModel.update(
confirmDeviceResult,
ConfirmDeviceCredential::class,
"$TAG#onConfirmDevice",
)
} }
} }
@@ -156,14 +174,21 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
ViewModelProvider(this, BackgroundViewModel.BackgroundViewModelFactory())[ ViewModelProvider(this, BackgroundViewModel.BackgroundViewModelFactory())[
BackgroundViewModel::class.java] BackgroundViewModel::class.java]
fingerprintFlowViewModel =
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
FingerprintFlowViewModel::class.java]
val interactor = val fingerprintSensorRepo =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val fingerprintManagerInteractor =
FingerprintManagerInteractorImpl( FingerprintManagerInteractorImpl(
context, context,
backgroundDispatcher, backgroundDispatcher,
fingerprintManager, fingerprintManager,
fingerprintSensorRepo,
GatekeeperPasswordProvider(LockPatternUtils(context)), GatekeeperPasswordProvider(LockPatternUtils(context)),
PressToAuthProviderImpl(context), PressToAuthRepoImpl(context),
enrollType, enrollType,
) )
@@ -171,27 +196,37 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN) val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(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 = gatekeeperViewModel =
ViewModelProvider( ViewModelProvider(
this, this,
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory( FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
gatekeeperInfo, gatekeeperInfo,
interactor, fingerprintManagerInteractor,
) ),
)[FingerprintGatekeeperViewModel::class.java] )[FingerprintGatekeeperViewModel::class.java]
navigationViewModel =
ViewModelProvider(
this,
FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
backgroundDispatcher,
interactor,
gatekeeperViewModel,
gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo,
enrollType,
)
)[FingerprintEnrollNavigationViewModel::class.java]
// Initialize FoldStateViewModel // Initialize FoldStateViewModel
foldStateViewModel = foldStateViewModel =
ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[ ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[
@@ -203,10 +238,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
ViewModelProvider( ViewModelProvider(
this, this,
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory( FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
interactor, fingerprintManagerInteractor,
gatekeeperViewModel, gatekeeperViewModel,
navigationViewModel, navigationViewModel,
) ),
)[FingerprintEnrollViewModel::class.java] )[FingerprintEnrollViewModel::class.java]
// Initialize scroll view model // Initialize scroll view model
@@ -220,7 +255,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
this, this,
AccessibilityViewModel.AccessibilityViewModelFactory( AccessibilityViewModel.AccessibilityViewModelFactory(
getSystemService(AccessibilityManager::class.java)!! getSystemService(AccessibilityManager::class.java)!!
) ),
)[AccessibilityViewModel::class.java] )[AccessibilityViewModel::class.java]
// Initialize OrientationViewModel // Initialize OrientationViewModel
@@ -234,8 +269,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
this, this,
FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory( FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory(
fingerprintEnrollViewModel, fingerprintEnrollViewModel,
backgroundViewModel backgroundViewModel,
) ),
)[FingerprintEnrollEnrollingViewModel::class.java] )[FingerprintEnrollEnrollingViewModel::class.java]
// Initialize FingerprintEnrollFindSensorViewModel // Initialize FingerprintEnrollFindSensorViewModel
@@ -248,36 +283,48 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
backgroundViewModel, backgroundViewModel,
accessibilityViewModel, accessibilityViewModel,
foldStateViewModel, foldStateViewModel,
orientationStateViewModel orientationStateViewModel,
) fingerprintFlowViewModel,
),
)[FingerprintEnrollFindSensorViewModel::class.java] )[FingerprintEnrollFindSensorViewModel::class.java]
// Initialize RFPS View Model // Initialize RFPS View Model
ViewModelProvider( ViewModelProvider(
this, this,
RFPSViewModel.RFPSViewModelFactory(fingerprintEnrollEnrollingViewModel) RFPSViewModel.RFPSViewModelFactory(fingerprintEnrollEnrollingViewModel, navigationViewModel),
)[RFPSViewModel::class.java] )[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 { lifecycleScope.launch {
navigationViewModel.navigationViewModel navigationViewModel.navigateTo.filterNotNull().collect { step ->
.filterNotNull() if (step is ConfirmDeviceCredential) {
.combine(fingerprintEnrollViewModel.sensorType) { nav, sensorType -> Pair(nav, sensorType) } launchConfirmOrChooseLock(userId)
.collect { (nav, sensorType) -> navigationViewModel.update(
Log.d(TAG, "navigationStep $nav") FingerprintAction.TRANSITION_FINISHED,
fingerprintEnrollViewModel.sensorTypeCached = sensorType TransitionStep::class,
val isForward = nav.forward "$TAG#launchConfirmOrChooseLock",
val currStep = nav.currStep )
val theClass: Class<Fragment>? = } else {
when (currStep) { val theClass: Fragment? =
Confirmation -> FingerprintEnrollConfirmationV2Fragment::class.java as Class<Fragment> when (step) {
Education -> FingerprintEnrollFindSensorV2Fragment::class.java as Class<Fragment> Confirmation -> FingerprintEnrollConfirmationV2Fragment()
is Education -> {
FingerprintEnrollFindSensorV2Fragment(step.sensor.sensorType)
}
is Enrollment -> { is Enrollment -> {
when (sensorType) { when (step.sensor.sensorType) {
FingerprintSensorType.REAR -> RFPSEnrollFragment::class.java as Class<Fragment> FingerprintSensorType.REAR -> RFPSEnrollFragment()
else -> FingerprintEnrollEnrollingV2Fragment::class.java as Class<Fragment> else -> FingerprintEnrollEnrollingV2Fragment()
} }
} }
Intro -> FingerprintEnrollIntroV2Fragment::class.java as Class<Fragment> Introduction -> FingerprintEnrollIntroV2Fragment()
else -> null else -> null
} }
@@ -291,18 +338,30 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
.setReorderingAllowed(true) .setReorderingAllowed(true)
.add(R.id.fragment_container_view, theClass, null) .add(R.id.fragment_container_view, theClass, null)
.commit() .commit()
} else { navigationViewModel.update(
FingerprintAction.TRANSITION_FINISHED,
TransitionStep::class,
"$TAG#fragmentManager.add($theClass)",
)
}
}
}
}
if (currStep is Finish) { lifecycleScope.launch {
if (currStep.resultCode != null) { navigationViewModel.shouldFinish.filterNotNull().collect {
finishActivity(currStep.resultCode) Log.d(TAG, "FingerprintSettingsNav.finishing($it)")
if (it.result != null) {
finishActivity(it.result as Int)
} else { } else {
finish() 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( overridePendingTransition(
com.google.android.setupdesign.R.anim.sud_slide_next_in, 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 android.os.Bundle
import androidx.fragment.app.Fragment 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. * A fragment to indicate that fingerprint enrollment has been completed.
@@ -31,9 +29,5 @@ class FingerprintEnrollConfirmationV2Fragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.android.settings.R 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. */ /** A fragment that is responsible for enrolling a users fingerprint. */
class FingerprintEnrollEnrollingV2Fragment : Fragment(R.layout.fingerprint_enroll_enrolling) { class FingerprintEnrollEnrollingV2Fragment : Fragment(R.layout.fingerprint_enroll_enrolling) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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] * 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
* will work. * 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. // This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
private var animation: FingerprintFindSensorAnimation? = null private var animation: FingerprintFindSensorAnimation? = null
@@ -62,7 +62,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? { ): View? {
val sensorType = val sensorType =
@@ -104,8 +104,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
} }
lifecycleScope.launch { lifecycleScope.launch {
viewModel.showRfpsAnimation.collect { viewModel.showRfpsAnimation.collect {
animation = animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
view.findViewById(R.id.fingerprint_sensor_location_animation)
animation!!.startAnimation() animation!!.startAnimation()
} }
} }
@@ -128,14 +127,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
footerBarMixin.secondaryButton = footerBarMixin.secondaryButton =
FooterButton.Builder(requireActivity()) FooterButton.Builder(requireActivity())
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip) .setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
.setListener { .setListener { viewModel.secondaryButtonClicked() }
run {
// TODO: Show the dialog for suw
Log.d(TAG, "onSkipClicked")
// TODO: Finish activity in the root activity instead.
requireActivity().finish()
}
}
.setButtonType(FooterButton.ButtonType.SKIP) .setButtonType(FooterButton.ButtonType.SKIP)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary) .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
.build() .build()
@@ -146,11 +138,9 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
FooterButton.Builder(requireActivity()) FooterButton.Builder(requireActivity())
.setText(R.string.security_settings_udfps_enroll_find_sensor_start_button) .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
.setListener { .setListener {
run {
Log.d(TAG, "onStartButtonClick") Log.d(TAG, "onStartButtonClick")
viewModel.proceedToEnrolling() viewModel.proceedToEnrolling()
} }
}
.setButtonType(FooterButton.ButtonType.NEXT) .setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
.build() .build()
@@ -159,7 +149,7 @@ class FingerprintEnrollFindSensorV2Fragment : Fragment() {
private fun setupLottie( private fun setupLottie(
view: View, view: View,
lottieAnimation: Int, lottieAnimation: Int,
lottieClickListener: View.OnClickListener? = null lottieClickListener: View.OnClickListener? = null,
) { ) {
val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie) val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
illustrationLottie?.setAnimation(lottieAnimation) illustrationLottie?.setAnimation(lottieAnimation)

View File

@@ -24,7 +24,6 @@ import android.graphics.PorterDuffColorFilter
import android.os.Bundle import android.os.Bundle
import android.text.Html import android.text.Html
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -36,9 +35,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.shared.model.Unicorn import com.android.settings.biometrics.fingerprint2.lib.model.Unicorn
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.FingerprintGatekeeperViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
import com.android.systemui.biometrics.shared.model.FingerprintSensorType 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.template.RequireScrollMixin
import com.google.android.setupdesign.util.DynamicColorPalette import com.google.android.setupdesign.util.DynamicColorPalette
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private const val TAG = "FingerprintEnrollmentIntroV2Fragment" 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 footerBarMixin: FooterBarMixin
private lateinit var textModel: TextModel private lateinit var textModel: TextModel
// Note that the ViewModels cannot be requested before the onCreate call private val viewModel: FingerprintEnrollIntroViewModel by lazy {
private val navigationViewModel: FingerprintEnrollNavigationViewModel by lazy { viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
viewModelProvider[FingerprintEnrollNavigationViewModel::class.java]
}
private val fingerprintViewModel: FingerprintEnrollViewModel by lazy {
viewModelProvider[FingerprintEnrollViewModel::class.java]
} }
private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy { private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy {
viewModelProvider[FingerprintScrollViewModel::class.java] viewModelProvider[FingerprintScrollViewModel::class.java]
} }
private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy { private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy {
viewModelProvider[FingerprintGatekeeperViewModel::class.java] viewModelProvider[FingerprintGatekeeperViewModel::class.java]
} }
@@ -113,17 +110,16 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? = ): View? =
super.onCreateView(inflater, container, savedInstanceState).also { theView -> super.onCreateView(inflater, container, savedInstanceState).also { theView ->
val view = theView!! val view = theView!!
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
combine( combine(viewModel.fingerprintFlow, viewModel.sensor.filterNotNull()) {
navigationViewModel.fingerprintFlow, enrollType,
fingerprintViewModel.sensorType, sensorType ->
) { enrollType, sensorType -> Pair(enrollType, sensorType.sensorType)
Pair(enrollType, sensorType)
} }
.collect { (enrollType, sensorType) -> .collect { (enrollType, sensorType) ->
textModel = textModel =
@@ -147,7 +143,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
R.id.icon_trash_can, R.id.icon_trash_can,
R.id.icon_info, R.id.icon_info,
R.id.icon_shield, R.id.icon_shield,
R.id.icon_link R.id.icon_link,
) )
.forEach { icon -> .forEach { icon ->
view.requireViewById<ImageView>(icon).drawable.colorFilter = colorFilter view.requireViewById<ImageView>(icon).drawable.colorFilter = colorFilter
@@ -186,31 +182,24 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
return view 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) { private fun setFooterLink(view: View) {
val footerLink: TextView = view.requireViewById(R.id.footer_learn_more) val footerLink: TextView = view.requireViewById(R.id.footer_learn_more)
footerLink.movementMethod = LinkMovementMethod.getInstance() footerLink.movementMethod = LinkMovementMethod.getInstance()
footerLink.text = footerLink.text =
Html.fromHtml( Html.fromHtml(
getString(R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more), 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( private fun setupFooterBarAndScrollView(view: View) {
view: View,
) {
val scrollView: ScrollView = val scrollView: ScrollView =
view.requireViewById(com.google.android.setupdesign.R.id.sud_scroll_view) view.requireViewById(com.google.android.setupdesign.R.id.sud_scroll_view)
scrollView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES scrollView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
// Next button responsible for starting the next fragment. // Next button responsible for starting the next fragment.
val onNextButtonClick: View.OnClickListener = val onNextButtonClick: View.OnClickListener =
View.OnClickListener { View.OnClickListener { viewModel.primaryButtonClicked() }
Log.d(TAG, "OnNextClicked")
navigationViewModel.nextStep()
}
val layout: GlifLayout = view.findViewById(R.id.setup_wizard_layout)!! val layout: GlifLayout = view.findViewById(R.id.setup_wizard_layout)!!
footerBarMixin = layout.getMixin(FooterBarMixin::class.java) footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
@@ -225,11 +214,11 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
footerBarMixin.setSecondaryButton( footerBarMixin.setSecondaryButton(
FooterButton.Builder(requireContext()) FooterButton.Builder(requireContext())
.setText(textModel.negativeButton) .setText(textModel.negativeButton)
.setListener({ Log.d(TAG, "prevClicked") }) .setListener { viewModel.onSecondaryButtonClicked() }
.setButtonType(FooterButton.ButtonType.NEXT) .setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
.build(), .build(),
true /* usePrimaryStyle */ true, /* usePrimaryStyle */
) )
val primaryButton = footerBarMixin.primaryButton val primaryButton = footerBarMixin.primaryButton
@@ -242,7 +231,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
requireContext(), requireContext(),
primaryButton, primaryButton,
R.string.security_settings_face_enroll_introduction_more, R.string.security_settings_face_enroll_introduction_more,
onNextButtonClick onNextButtonClick,
) )
requireScrollMixin.setOnRequireScrollStateChangedListener { scrollNeeded: Boolean -> requireScrollMixin.setOnRequireScrollStateChangedListener { scrollNeeded: Boolean ->
@@ -257,7 +246,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
if (consented) { if (consented) {
primaryButton.setText( primaryButton.setText(
requireContext(), requireContext(),
R.string.security_settings_fingerprint_enroll_introduction_agree R.string.security_settings_fingerprint_enroll_introduction_agree,
) )
secondaryButton.visibility = View.VISIBLE secondaryButton.visibility = View.VISIBLE
} else { } else {
@@ -309,7 +298,7 @@ class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enro
private fun getIconColorFilter(): PorterDuffColorFilter { private fun getIconColorFilter(): PorterDuffColorFilter {
return PorterDuffColorFilter( return PorterDuffColorFilter(
DynamicColorPalette.getColor(context, DynamicColorPalette.ColorType.ACCENT), 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.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R 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.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.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.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.IconTouchDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar 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.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.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.core.instrumentation.InstrumentedDialogFragment import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterBarMixin
@@ -49,8 +51,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private const val TAG = "RFPSEnrollFragment"
/** This fragment is responsible for taking care of rear fingerprint enrollment. */ /** This fragment is responsible for taking care of rear fingerprint enrollment. */
class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrolling) { 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 { private val backgroundViewModel: BackgroundViewModel by lazy {
ViewModelProvider(requireActivity())[BackgroundViewModel::class.java] ViewModelProvider(requireActivity())[BackgroundViewModel::class.java]
} }
private val navigationViewModel: FingerprintNavigationViewModel by lazy {
ViewModelProvider(requireActivity())[FingerprintNavigationViewModel::class.java]
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? { ): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)!! val view = super.onCreateView(inflater, container, savedInstanceState)!!
val fragment = this val fragment = this
@@ -99,7 +102,7 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
footerBarMixin.secondaryButton = footerBarMixin.secondaryButton =
FooterButton.Builder(context) FooterButton.Builder(context)
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip) .setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
.setListener { Log.e(TAG, "skip enrollment!") } .setListener { rfpsViewModel.negativeButtonClicked() }
.setButtonType(FooterButton.ButtonType.SKIP) .setButtonType(FooterButton.ButtonType.SKIP)
.setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary) .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
.build() .build()
@@ -150,7 +153,7 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
backgroundViewModel.background backgroundViewModel.background
.filter { inBackground -> inBackground } .filter { inBackground -> inBackground }
.collect { rfpsViewModel.stopEnrollment() } .collect { rfpsViewModel.didGoToBackground() }
} }
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
@@ -171,7 +174,6 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
.setDuration(200) .setDuration(200)
.setInterpolator(linearOutSlowInInterpolator) .setInterpolator(linearOutSlowInInterpolator)
.start() .start()
} }
} }
@@ -207,6 +209,12 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
dismissDialogs() dismissDialogs()
} }
} }
viewLifecycleOwner.lifecycleScope.launch {
rfpsViewModel.didCompleteEnrollment
.filter { it }
.collect { rfpsViewModel.finishedSuccessfully() }
}
return view return view
} }
@@ -215,28 +223,18 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
try { try {
val shouldRestartEnrollment = FingerprintErrorDialog.showInstance(error, fragment) val shouldRestartEnrollment = FingerprintErrorDialog.showInstance(error, fragment)
rfpsViewModel.userClickedStopEnrollDialog()
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Exception occurred $exception") Log.e(TAG, "Exception occurred $exception")
} }
onEnrollmentFailed()
} }
} }
private fun onEnrollmentFailed() {
rfpsViewModel.stopEnrollment()
}
private fun handleEnrollProgress(progress: FingerEnrollState.EnrollProgress) { private fun handleEnrollProgress(progress: FingerEnrollState.EnrollProgress) {
progressBar.updateProgress( progressBar.updateProgress(
progress.remainingSteps.toFloat() / progress.totalStepsRequired.toFloat() progress.remainingSteps.toFloat() / progress.totalStepsRequired.toFloat()
) )
if (progress.remainingSteps == 0) {
performNextStepSuccess()
} }
}
private fun performNextStepSuccess() {}
private fun dismissDialogs() { private fun dismissDialogs() {
val transaction = parentFragmentManager.beginTransaction() val transaction = parentFragmentManager.beginTransaction()
@@ -249,4 +247,9 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
} }
transaction.commitAllowingStateLoss() 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) private val _touches: MutableStateFlow<Int> = MutableStateFlow(0)
/** /**
* Whether or not the UI should be showing the dialog. By making this SharingStarted.Eagerly * Whether or not the UI should be showing the dialog. By making this SharingStarted.Eagerly the
* the first event 0 % 3 == 0 will fire as soon as this view model is created, so it should * first event 0 % 3 == 0 will fire as soon as this view model is created, so it should be ignored
* be ignored and work as intended. * and work as intended.
*/ */
val shouldShowDialog: Flow<Boolean> = val shouldShowDialog: Flow<Boolean> =
_touches _touches

View File

@@ -19,13 +19,17 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope 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.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.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.transform
@@ -34,9 +38,10 @@ import kotlinx.coroutines.flow.update
/** View Model used by the rear fingerprint enrollment fragment. */ /** View Model used by the rear fingerprint enrollment fragment. */
class RFPSViewModel( class RFPSViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel, private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModel() { ) : 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) private val _textViewIsVisible = MutableStateFlow<Boolean>(false)
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow() val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
@@ -61,9 +66,8 @@ class RFPSViewModel(
val helpMessage: Flow<FingerEnrollState.EnrollHelp?> = val helpMessage: Flow<FingerEnrollState.EnrollHelp?> =
enrollFlow enrollFlow
.filterIsInstance<FingerEnrollState.EnrollHelp>() .filterIsInstance<FingerEnrollState.EnrollHelp>()
.shareIn(viewModelScope, SharingStarted.Eagerly, 0).transform { .shareIn(viewModelScope, SharingStarted.Eagerly, 0)
_textViewIsVisible.update { true } .transform { _textViewIsVisible.update { true } }
}
/** /**
* The error message should only be shown once, for scenarios like screen rotations, we don't want * 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>() .filterIsInstance<FingerEnrollState.EnrollError>()
.shareIn(viewModelScope, SharingStarted.Eagerly, 0) .shareIn(viewModelScope, SharingStarted.Eagerly, 0)
val didCompleteEnrollment: Flow<Boolean> = progress.filterNotNull().map { it.remainingSteps == 0 }
/** Indicates if the consumer is ready for enrollment */ /** Indicates if the consumer is ready for enrollment */
fun readyForEnrollment() { fun readyForEnrollment() {
fingerprintEnrollViewModel.canEnroll() fingerprintEnrollViewModel.canEnroll()
@@ -88,15 +94,51 @@ class RFPSViewModel(
_textViewIsVisible.update { isVisible } _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( class RFPSViewModelFactory(
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create( override fun <T : ViewModel> create(modelClass: Class<T>): T {
modelClass: Class<T>, return RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel) as T
): T {
return RFPSViewModel(fingerprintEnrollEnrollingViewModel) 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 android.util.Log
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.android.settings.R 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 com.android.settings.core.instrumentation.InstrumentedDialogFragment
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
@@ -86,10 +86,8 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
private const val KEY_TITLE = "fingerprint_title" private const val KEY_TITLE = "fingerprint_title"
private const val KEY_SHOULD_TRY_AGAIN = "should_try_again" private const val KEY_SHOULD_TRY_AGAIN = "should_try_again"
suspend fun showInstance( suspend fun showInstance(error: FingerEnrollState.EnrollError, fragment: Fragment) =
error: FingerEnrollState.EnrollError, suspendCancellableCoroutine { continuation ->
fragment: Fragment,
) = suspendCancellableCoroutine { continuation ->
val dialog = FingerprintErrorDialog() val dialog = FingerprintErrorDialog()
dialog.onTryAgain = DialogInterface.OnClickListener { _, _ -> continuation.resume(true) } dialog.onTryAgain = DialogInterface.OnClickListener { _, _ -> continuation.resume(true) }
@@ -104,18 +102,9 @@ class FingerprintErrorDialog : InstrumentedDialogFragment() {
continuation.invokeOnCancellation { Log.d(TAG, "invokeOnCancellation $dialog") } continuation.invokeOnCancellation { Log.d(TAG, "invokeOnCancellation $dialog") }
val bundle = Bundle() val bundle = Bundle()
bundle.putInt( bundle.putInt(KEY_TITLE, error.errTitle)
KEY_TITLE, bundle.putInt(KEY_MESSAGE, error.errString)
error.errTitle, bundle.putBoolean(KEY_SHOULD_TRY_AGAIN, error.shouldRetryEnrollment)
)
bundle.putInt(
KEY_MESSAGE,
error.errString,
)
bundle.putBoolean(
KEY_SHOULD_TRY_AGAIN,
error.shouldRetryEnrollment,
)
dialog.arguments = bundle dialog.arguments = bundle
Log.d(TAG, "showing dialog $dialog") Log.d(TAG, "showing dialog $dialog")
dialog.show(fragment.parentFragmentManager, FingerprintErrorDialog::class.java.toString()) dialog.show(fragment.parentFragmentManager, FingerprintErrorDialog::class.java.toString())

View File

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

View File

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

View File

@@ -19,8 +19,10 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope 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.shared.model.SetupWizard 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 com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,20 +38,22 @@ import kotlinx.coroutines.launch
/** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */ /** Models the UI state for [FingerprintEnrollFindSensorV2Fragment]. */
class FingerprintEnrollFindSensorViewModel( class FingerprintEnrollFindSensorViewModel(
private val navigationViewModel: FingerprintEnrollNavigationViewModel, private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel, private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel, private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
backgroundViewModel: BackgroundViewModel, backgroundViewModel: BackgroundViewModel,
accessibilityViewModel: AccessibilityViewModel, accessibilityViewModel: AccessibilityViewModel,
foldStateViewModel: FoldStateViewModel, foldStateViewModel: FoldStateViewModel,
orientationStateViewModel: OrientationStateViewModel orientationStateViewModel: OrientationStateViewModel,
fingerprintFlowViewModel: FingerprintFlowViewModel,
) : ViewModel() { ) : ViewModel() {
/** Represents the stream of sensor type. */ /** Represents the stream of sensor type. */
val sensorType: Flow<FingerprintSensorType> = val sensorType: Flow<FingerprintSensorType> =
fingerprintEnrollViewModel.sensorType.shareIn( fingerprintEnrollViewModel.sensorType.shareIn(
viewModelScope, viewModelScope,
SharingStarted.WhileSubscribed(), SharingStarted.WhileSubscribed(),
1 1,
) )
private val _isUdfps: Flow<Boolean> = private val _isUdfps: Flow<Boolean> =
sensorType.map { sensorType.map {
@@ -103,10 +107,10 @@ class FingerprintEnrollFindSensorViewModel(
fingerprintEnrollViewModel.sensorType, fingerprintEnrollViewModel.sensorType,
gatekeeperViewModel.hasValidGatekeeperInfo, gatekeeperViewModel.hasValidGatekeeperInfo,
gatekeeperViewModel.gatekeeperInfo, gatekeeperViewModel.gatekeeperInfo,
navigationViewModel.navigationViewModel navigationViewModel.currentScreen,
) { sensorType, hasValidGatekeeperInfo, gatekeeperInfo, navigationViewModel -> ) { sensorType, hasValidGatekeeperInfo, gatekeeperInfo, currStep ->
val shouldStartEnroll = val shouldStartEnroll =
navigationViewModel.currStep == Education && currStep is Education &&
sensorType != FingerprintSensorType.UDFPS_OPTICAL && sensorType != FingerprintSensorType.UDFPS_OPTICAL &&
sensorType != FingerprintSensorType.UDFPS_ULTRASONIC && sensorType != FingerprintSensorType.UDFPS_ULTRASONIC &&
hasValidGatekeeperInfo hasValidGatekeeperInfo
@@ -128,22 +132,19 @@ class FingerprintEnrollFindSensorViewModel(
// Only collect the flow when we should be running. // Only collect the flow when we should be running.
if (it) { if (it) {
combine( combine(
navigationViewModel.fingerprintFlow,
fingerprintEnrollViewModel.educationEnrollFlow.filterNotNull(), fingerprintEnrollViewModel.educationEnrollFlow.filterNotNull(),
) { enrollType, educationFlow -> fingerprintFlowViewModel.fingerprintFlow,
Pair(enrollType, educationFlow) ) { educationFlow, type ->
Pair(educationFlow, type)
} }
.collect { (enrollType, educationFlow) -> .collect { (educationFlow, type) ->
when (educationFlow) { 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.EnrollProgress -> proceedToEnrolling()
is FingerEnrollState.EnrollError -> { is FingerEnrollState.EnrollError -> {
if (educationFlow.isCancelled) { if (educationFlow.isCancelled) {
proceedToEnrolling() proceedToEnrolling()
} else { } else {
_showErrorDialog.update { Pair(educationFlow.errString, enrollType == SetupWizard) } _showErrorDialog.update { Pair(educationFlow.errString, type == SetupWizard) }
} }
} }
is FingerEnrollState.EnrollHelp -> {} is FingerEnrollState.EnrollHelp -> {}
@@ -169,17 +170,28 @@ class FingerprintEnrollFindSensorViewModel(
/** Proceed to EnrollEnrolling page. */ /** Proceed to EnrollEnrolling page. */
fun proceedToEnrolling() { 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( class FingerprintEnrollFindSensorViewModelFactory(
private val navigationViewModel: FingerprintEnrollNavigationViewModel, private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel, private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel, private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
private val backgroundViewModel: BackgroundViewModel, private val backgroundViewModel: BackgroundViewModel,
private val accessibilityViewModel: AccessibilityViewModel, private val accessibilityViewModel: AccessibilityViewModel,
private val foldStateViewModel: FoldStateViewModel, private val foldStateViewModel: FoldStateViewModel,
private val orientationStateViewModel: OrientationStateViewModel private val orientationStateViewModel: OrientationStateViewModel,
private val fingerprintFlowViewModel: FingerprintFlowViewModel,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T { override fun <T : ViewModel> create(modelClass: Class<T>): T {
@@ -190,9 +202,15 @@ class FingerprintEnrollFindSensorViewModel(
backgroundViewModel, backgroundViewModel,
accessibilityViewModel, accessibilityViewModel,
foldStateViewModel, foldStateViewModel,
orientationStateViewModel orientationStateViewModel,
fingerprintFlowViewModel,
) )
as T 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.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope 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 com.android.settings.biometrics.fingerprint2.shared.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
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.FingerprintNavigationStep.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -34,7 +36,7 @@ import kotlinx.coroutines.flow.transformLatest
class FingerprintEnrollViewModel( class FingerprintEnrollViewModel(
private val fingerprintManagerInteractor: FingerprintManagerInteractor, private val fingerprintManagerInteractor: FingerprintManagerInteractor,
gatekeeperViewModel: FingerprintGatekeeperViewModel, gatekeeperViewModel: FingerprintGatekeeperViewModel,
navigationViewModel: FingerprintEnrollNavigationViewModel, val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModel() { ) : ViewModel() {
/** /**
@@ -45,8 +47,8 @@ class FingerprintEnrollViewModel(
*/ */
var sensorTypeCached: FingerprintSensorType? = null var sensorTypeCached: FingerprintSensorType? = null
private var _enrollReason: Flow<EnrollReason?> = private var _enrollReason: Flow<EnrollReason?> =
navigationViewModel.navigationViewModel.map { navigationViewModel.currentScreen.map {
when (it.currStep) { when (it) {
is Enrollment -> EnrollReason.EnrollEnrolling is Enrollment -> EnrollReason.EnrollEnrolling
is Education -> EnrollReason.FindSensor is Education -> EnrollReason.FindSensor
else -> null else -> null
@@ -64,8 +66,7 @@ class FingerprintEnrollViewModel(
* This flow should be the only flow which calls enroll(). * This flow should be the only flow which calls enroll().
*/ */
val _enrollFlow: Flow<FingerEnrollState> = val _enrollFlow: Flow<FingerEnrollState> =
combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason, combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason ->
->
Pair(hardwareAuthToken, enrollReason) Pair(hardwareAuthToken, enrollReason)
} }
.transformLatest { .transformLatest {
@@ -110,18 +111,11 @@ class FingerprintEnrollViewModel(
class FingerprintEnrollViewModelFactory( class FingerprintEnrollViewModelFactory(
val interactor: FingerprintManagerInteractor, val interactor: FingerprintManagerInteractor,
val gatekeeperViewModel: FingerprintGatekeeperViewModel, val gatekeeperViewModel: FingerprintGatekeeperViewModel,
val navigationViewModel: FingerprintEnrollNavigationViewModel, val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create( override fun <T : ViewModel> create(modelClass: Class<T>): T {
modelClass: Class<T>, return FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) as 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.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope 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.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -29,11 +29,11 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private const val TAG = "FingerprintGatekeeperViewModel"
sealed interface GatekeeperInfo { sealed interface GatekeeperInfo {
object Invalid : GatekeeperInfo object Invalid : GatekeeperInfo
object Timeout : GatekeeperInfo object Timeout : GatekeeperInfo
data class GatekeeperPasswordInfo(val token: ByteArray?, val passwordHandle: Long?) : data class GatekeeperPasswordInfo(val token: ByteArray?, val passwordHandle: Long?) :
GatekeeperInfo 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 { companion object {
private const val TAG = "FingerprintGatekeeperViewModel"
/** /**
* A function that checks if the challenge and token are valid, in which case a * A function that checks if the challenge and token are valid, in which case a
* [GatekeeperInfo.GatekeeperPasswordInfo] is provided, else [GatekeeperInfo.Invalid] * [GatekeeperInfo.GatekeeperPasswordInfo] is provided, else [GatekeeperInfo.Invalid]
@@ -110,17 +122,4 @@ class FingerprintGatekeeperViewModel(
return GatekeeperInfo.GatekeeperPasswordInfo(token, challenge) 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 { class FingerprintScrollViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create( override fun <T : ViewModel> create(modelClass: Class<T>): T {
modelClass: Class<T>,
): T {
return FingerprintScrollViewModel() as T return FingerprintScrollViewModel() as T
} }
} }

View File

@@ -49,9 +49,7 @@ class FoldStateViewModel(context: Context) : ViewModel() {
class FoldStateViewModelFactory(private val context: Context) : ViewModelProvider.Factory { class FoldStateViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create( override fun <T : ViewModel> create(modelClass: Class<T>): T {
modelClass: Class<T>,
): T {
return FoldStateViewModel(context) as 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( .stateIn(
viewModelScope, // This is going to tied to the view model scope viewModelScope, // This is going to tied to the view model scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
context.display!!.rotation context.display!!.rotation,
) )
fun getRotationFromDefault(rotation: Int): Int { fun getRotationFromDefault(rotation: Int): Int {
@@ -73,9 +73,7 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
class OrientationViewModelFactory(private val context: Context) : ViewModelProvider.Factory { class OrientationViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create( override fun <T : ViewModel> create(modelClass: Class<T>): T {
modelClass: Class<T>,
): T {
return OrientationStateViewModel(context) as 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.hardware.fingerprint.FingerprintManager
import android.util.Log import android.util.Log
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
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.binder.FingerprintSettingsViewBinder.FingerprintView 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.EnrollAdditionalFingerprint
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollFirstFingerprint import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.EnrollFirstFingerprint
@@ -52,7 +52,7 @@ object FingerprintSettingsViewBinder {
userId: Int, userId: Int,
gateKeeperPasswordHandle: Long?, gateKeeperPasswordHandle: Long?,
challenge: Long?, challenge: Long?,
challengeToken: ByteArray? challengeToken: ByteArray?,
) )
/** Helper to launch an add fingerprint request */ /** Helper to launch an add fingerprint request */
fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?) fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?)
@@ -158,7 +158,7 @@ object FingerprintSettingsViewBinder {
nextStep.userId, nextStep.userId,
nextStep.gateKeeperPasswordHandle, nextStep.gateKeeperPasswordHandle,
nextStep.challenge, nextStep.challenge,
nextStep.challengeToken nextStep.challengeToken,
) )
is EnrollAdditionalFingerprint -> is EnrollAdditionalFingerprint ->
view.launchAddFingerprint(nextStep.userId, nextStep.challengeToken) view.launchAddFingerprint(nextStep.userId, nextStep.challengeToken)

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ import android.util.Log
import android.widget.ImeAwareEditText import android.widget.ImeAwareEditText
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.android.settings.R 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 com.android.settings.core.instrumentation.InstrumentedDialogFragment
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
@@ -78,7 +78,7 @@ class FingerprintSettingsRenameDialog : InstrumentedDialogFragment() {
end: Int, end: Int,
dest: Spanned?, dest: Spanned?,
dstart: Int, dstart: Int,
dend: Int dend: Int,
): CharSequence? { ): CharSequence? {
for (index in start until end) { for (index in start until end) {
val c = source[index] val c = source[index]
@@ -133,13 +133,13 @@ class FingerprintSettingsRenameDialog : InstrumentedDialogFragment() {
val bundle = Bundle() val bundle = Bundle()
bundle.putObject( bundle.putObject(
KEY_FINGERPRINT, 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 dialog.arguments = bundle
Log.d(TAG, "showing dialog $dialog") Log.d(TAG, "showing dialog $dialog")
dialog.show( dialog.show(
target.parentFragmentManager, 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.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal 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.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.repository.PressToAuthProviderImpl import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import com.android.settings.biometrics.fingerprint2.shared.model.Settings
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder 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.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
@@ -128,12 +129,12 @@ class FingerprintSettingsV2Fragment :
if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) { if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
navigationViewModel.onEnrollFirstFailure( navigationViewModel.onEnrollFirstFailure(
"Received RESULT_TIMEOUT when enrolling", "Received RESULT_TIMEOUT when enrolling",
resultCode resultCode,
) )
} else { } else {
navigationViewModel.onEnrollFirstFailure( navigationViewModel.onEnrollFirstFailure(
"Incorrect resultCode or data was null", "Incorrect resultCode or data was null",
resultCode resultCode,
) )
} }
} else { } else {
@@ -212,21 +213,24 @@ class FingerprintSettingsV2Fragment :
context.contentResolver, context.contentResolver,
Secure.SFPS_PERFORMANT_AUTH_ENABLED, Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn, toReturn,
userHandle userHandle,
) )
} }
toReturn == 1 toReturn == 1
} }
val fingerprintSensorProvider =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val interactor = val interactor =
FingerprintManagerInteractorImpl( FingerprintManagerInteractorImpl(
context.applicationContext, context.applicationContext,
backgroundDispatcher, backgroundDispatcher,
fingerprintManager, fingerprintManager,
fingerprintSensorProvider,
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)), GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
PressToAuthProviderImpl(context), PressToAuthRepoImpl(context),
isAnySuw Settings,
) )
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN) val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
@@ -240,8 +244,8 @@ class FingerprintSettingsV2Fragment :
interactor, interactor,
backgroundDispatcher, backgroundDispatcher,
token, token,
challenge challenge,
) ),
)[FingerprintSettingsNavigationViewModel::class.java] )[FingerprintSettingsNavigationViewModel::class.java]
settingsViewModel = settingsViewModel =
@@ -252,15 +256,10 @@ class FingerprintSettingsV2Fragment :
interactor, interactor,
backgroundDispatcher, backgroundDispatcher,
navigationViewModel, navigationViewModel,
) ),
)[FingerprintSettingsViewModel::class.java] )[FingerprintSettingsViewModel::class.java]
FingerprintSettingsViewBinder.bind( FingerprintSettingsViewBinder.bind(this, settingsViewModel, navigationViewModel, lifecycleScope)
this,
settingsViewModel,
navigationViewModel,
lifecycleScope,
)
} }
override fun getMetricsCategory(): Int { override fun getMetricsCategory(): Int {
@@ -364,7 +363,7 @@ class FingerprintSettingsV2Fragment :
RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
activity, activity,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT,
requireActivity().userId requireActivity().userId,
) )
val activity = requireActivity() val activity = requireActivity()
val helpIntent = val helpIntent =
@@ -404,7 +403,7 @@ class FingerprintSettingsV2Fragment :
column.title = column.title =
getString( getString(
R.string.security_settings_fingerprint_enroll_introduction_v3_message, R.string.security_settings_fingerprint_enroll_introduction_v3_message,
DeviceHelper.getDeviceName(requireActivity()) DeviceHelper.getDeviceName(requireActivity()),
) )
column.learnMoreOnClickListener = learnMoreClickListener column.learnMoreOnClickListener = learnMoreClickListener
column.learnMoreOverrideText = column.learnMoreOverrideText =
@@ -437,13 +436,12 @@ class FingerprintSettingsV2Fragment :
val willDelete = val willDelete =
fingerprintPreferences() fingerprintPreferences()
.first { it?.fingerprintViewModel == fingerprintViewModel } .first { it?.fingerprintViewModel == fingerprintViewModel }
?.askUserToDeleteDialog() ?.askUserToDeleteDialog() ?: false
?: false
if (willDelete) { if (willDelete) {
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
context, context,
SettingsEnums.ACTION_FINGERPRINT_DELETE, SettingsEnums.ACTION_FINGERPRINT_DELETE,
fingerprintViewModel.fingerId fingerprintViewModel.fingerId,
) )
} }
return willDelete return willDelete
@@ -466,7 +464,7 @@ class FingerprintSettingsV2Fragment :
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
context, context,
SettingsEnums.ACTION_FINGERPRINT_RENAME, SettingsEnums.ACTION_FINGERPRINT_RENAME,
toReturn.first.fingerId toReturn.first.fingerId,
) )
} }
return toReturn return toReturn
@@ -518,12 +516,12 @@ class FingerprintSettingsV2Fragment :
val intent = Intent() val intent = Intent()
intent.setClassName( intent.setClassName(
SETTINGS_PACKAGE_NAME, SETTINGS_PACKAGE_NAME,
FingerprintEnrollIntroductionInternal::class.java.name FingerprintEnrollIntroductionInternal::class.java.name,
) )
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true) intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true)
intent.putExtra( intent.putExtra(
SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE,
) )
intent.putExtra(Intent.EXTRA_USER_ID, userId) intent.putExtra(Intent.EXTRA_USER_ID, userId)
@@ -546,7 +544,7 @@ class FingerprintSettingsV2Fragment :
val intent = Intent() val intent = Intent()
intent.setClassName( intent.setClassName(
SETTINGS_PACKAGE_NAME, SETTINGS_PACKAGE_NAME,
FingerprintEnrollEnrolling::class.qualifiedName.toString() FingerprintEnrollEnrolling::class.qualifiedName.toString(),
) )
intent.putExtra(Intent.EXTRA_USER_ID, userId) intent.putExtra(Intent.EXTRA_USER_ID, userId)
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, challengeToken) intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, challengeToken)
@@ -568,8 +566,7 @@ class FingerprintSettingsV2Fragment :
return category?.let { cat -> return category?.let { cat ->
cat.childrenToList().map { it as FingerprintSettingsPreference? } cat.childrenToList().map { it as FingerprintSettingsPreference? }
} } ?: emptyList()
?: emptyList()
} }
private fun PreferenceCategory.childrenToList(): List<Preference> { private fun PreferenceCategory.childrenToList(): List<Preference> {

View File

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

View File

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

View File

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

View File

@@ -16,15 +16,11 @@
package com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel 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. */ /** Classed use to represent a Dialogs state. */
sealed class PreferenceViewModel { sealed class PreferenceViewModel {
data class RenameDialog( data class RenameDialog(val fingerprintViewModel: FingerprintData) : PreferenceViewModel()
val fingerprintViewModel: FingerprintData,
) : PreferenceViewModel()
data class DeleteDialog( data class DeleteDialog(val fingerprintViewModel: FingerprintData) : PreferenceViewModel()
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.espresso.matcher.ViewMatchers.withText
import androidx.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import com.android.settings.R 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.fragment.FingerprintEnrollIntroV2Fragment
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.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.FingerprintScrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo 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.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.GlifLayout
import com.google.android.setupdesign.template.RequireScrollMixin import com.google.android.setupdesign.template.RequireScrollMixin
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.StandardTestDispatcher
@@ -62,18 +66,27 @@ class FingerprintEnrollIntroFragmentTest {
) )
private val backgroundDispatcher = StandardTestDispatcher() private val backgroundDispatcher = StandardTestDispatcher()
private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment> 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 = private val navigationViewModel =
FingerprintEnrollNavigationViewModel( FingerprintNavigationViewModel(
backgroundDispatcher, Introduction,
interactor, false,
gatekeeperViewModel, flowViewModel,
Intro, interactor
NavState(true),
Default,
) )
private var fingerprintViewModel = private var fingerprintViewModel =
FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) FingerprintEnrollIntroViewModel(
navigationViewModel,
flowViewModel,
interactor,
)
private var fingerprintScrollViewModel = FingerprintScrollViewModel() private var fingerprintScrollViewModel = FingerprintScrollViewModel()
@Before @Before
@@ -85,9 +98,9 @@ class FingerprintEnrollIntroFragmentTest {
modelClass: Class<T>, modelClass: Class<T>,
): T { ): T {
return when (modelClass) { return when (modelClass) {
FingerprintEnrollViewModel::class.java -> fingerprintViewModel FingerprintEnrollIntroViewModel::class.java -> fingerprintViewModel
FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel FingerprintNavigationViewModel::class.java -> navigationViewModel
FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
else -> null else -> null
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ package com.android.settings.fingerprint2.ui.settings
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.BiometricEnrollBase 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.EnrollFirstFingerprint
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FinishSettings import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FinishSettings

View File

@@ -17,8 +17,8 @@
package com.android.settings.fingerprint2.ui.settings package com.android.settings.fingerprint2.ui.settings
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
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.FingerprintSettingsNavigationViewModel 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.FingerprintSettingsViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.PreferenceViewModel import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.PreferenceViewModel