Biometrics Enrollment refactor (7/N)

This cl moves the creation of repos and interactors to the
SettingsApplication.
Bug: 297082837
Test: atest

Change-Id: I9049da6f03bb1dc18d4186961444bf613d773d0e
This commit is contained in:
Joshua McCloskey
2024-05-01 22:30:25 +00:00
committed by Joshua Mccloskey
parent b2f88a16c9
commit 584b6c9e96
68 changed files with 1068 additions and 939 deletions

View File

@@ -108,6 +108,8 @@ android_library {
"telephony_flags_core_java_lib",
"setupdesign-lottie-loading-layout",
"device_policy_aconfig_flags_lib",
"kotlinx-coroutines-core",
"kotlinx-coroutines-android",
],
plugins: ["androidx.room_room-compiler-plugin"],

View File

@@ -24,9 +24,11 @@ import android.provider.Settings;
import android.util.FeatureFlagUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.biometrics.fingerprint2.BiometricsEnvironment;
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
import com.android.settings.development.DeveloperOptionsActivityLifecycle;
import com.android.settings.fuelgauge.BatterySettingsStorage;
@@ -47,6 +49,7 @@ import java.lang.ref.WeakReference;
public class SettingsApplication extends Application {
private WeakReference<SettingsHomepageActivity> mHomeActivity = new WeakReference<>(null);
private BiometricsEnvironment mBiometricsEnvironment;
@Override
protected void attachBaseContext(Context base) {
@@ -70,6 +73,7 @@ public class SettingsApplication extends Application {
// Set Spa environment.
setSpaEnvironment();
mBiometricsEnvironment = new BiometricsEnvironment(this);
if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this)
&& FeatureFlagUtils.isEnabled(this,
@@ -111,6 +115,11 @@ public class SettingsApplication extends Application {
return mHomeActivity.get();
}
@Nullable
public BiometricsEnvironment getBiometricEnvironment() {
return mBiometricsEnvironment;
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);

View File

@@ -184,7 +184,6 @@ public class UdfpsEnrollHelper extends InstrumentedFragment {
*/
public void onAcquired(boolean isAcquiredGood) {
if (mListener != null) {
Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep());
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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
import android.hardware.fingerprint.FingerprintManager
import android.view.MotionEvent
import android.view.accessibility.AccessibilityManager
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import com.android.internal.widget.LockPatternUtils
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.debug.data.repository.UdfpsEnrollDebugRepositoryImpl
import com.android.settings.biometrics.fingerprint2.debug.domain.interactor.DebugTouchEventInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import java.util.concurrent.Executors
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
/**
* This class should handle all repo & interactor creation needed by the ViewModels for the
* biometrics code.
*
* This code is instantiated within the [SettingsApplication], all repos should be private &
* immutable and all interactors should public and immutable
*/
class BiometricsEnvironment(context: SettingsApplication) : ViewModelStoreOwner {
private val executorService = Executors.newSingleThreadExecutor()
private val backgroundDispatcher = executorService.asCoroutineDispatcher()
private val applicationScope = MainScope()
private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context))
private val fingerprintManager =
context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager?
private val fingerprintSensorRepository: FingerprintSensorRepository =
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope)
private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl()
private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl()
/** For now, interactors are public to those with access to the [BiometricsEnvironment] class */
val fingerprintEnrollInteractor: FingerprintEnrollInteractor by lazy {
FingerprintEnrollInteractorImpl(context, fingerprintManager, Settings)
}
/** [FingerprintManagerInteractor] to be used to construct view models */
val fingerprintManagerInteractor: FingerprintManagerInteractor by lazy {
FingerprintManagerInteractorImpl(
context,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorRepository,
gateKeeperPasswordProvider,
fingerprintEnrollInteractor,
)
}
val accessibilityInteractor: AccessibilityInteractor by lazy {
AccessibilityInteractorImpl(
context.getSystemService(AccessibilityManager::class.java)!!,
applicationScope,
)
}
val foldStateInteractor: FoldStateInteractor by lazy { FoldStateInteractorImpl(context) }
val orientationInteractor: OrientationInteractor by lazy { OrientationInteractorImpl(context) }
val vibrationInteractor: VibrationInteractor by lazy { VibrationInteractorImpl(context) }
val displayDensityInteractor: DisplayDensityInteractor by lazy {
DisplayDensityInteractorImpl(context, applicationScope)
}
val debuggingInteractor: DebuggingInteractor by lazy {
DebuggingInteractorImpl(debuggingRepository)
}
val enrollStageInteractor: EnrollStageInteractor by lazy { EnrollStageInteractorImpl() }
val udfpsEnrollInteractor: UdfpsEnrollInteractor by lazy {
UdfpsEnrollInteractorImpl(context, accessibilityInteractor)
}
val sensorInteractor: FingerprintSensorInteractor by lazy {
FingerprintSensorInteractorImpl(fingerprintSensorRepository)
}
val touchEventInteractor: TouchEventInteractor by lazy {
if (debuggingRepository.isDebuggingEnabled()) {
DebugTouchEventInteractorImpl(udfpsDebugRepo)
} else {
object : TouchEventInteractor {
override val touchEvent: Flow<MotionEvent> = flowOf()
}
}
}
override val viewModelStore: ViewModelStore = ViewModelStore()
}

View File

@@ -23,6 +23,7 @@ interface DebuggingRepository {
/** A function that will return if a build is debuggable */
fun isDebuggingEnabled(): Boolean
/** A function that will return if udfps enrollment should be swapped with debug repos */
fun isUdfpsEnrollmentDebuggingEnabled(): Boolean
}

View File

@@ -46,7 +46,7 @@ interface FingerprintSensorRepository {
}
class FingerprintSensorRepositoryImpl(
fingerprintManager: FingerprintManager,
fingerprintManager: FingerprintManager?,
backgroundDispatcher: CoroutineDispatcher,
activityScope: CoroutineScope,
) : FingerprintSensorRepository {
@@ -73,12 +73,9 @@ class FingerprintSensorRepositoryImpl(
.stateIn(activityScope, started = SharingStarted.Eagerly, initialValue = DEFAULT_PROPS)
override val fingerprintSensor: Flow<FingerprintSensor> =
fingerprintPropsInternal.transform {
emit(it.toFingerprintSensor())
}
fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) }
companion object {
private const val TAG = "FingerprintSensorRepoImpl"
private val DEFAULT_PROPS =
FingerprintSensorPropertiesInternal(

View File

@@ -17,6 +17,7 @@
package com.android.settings.biometrics.fingerprint2.data.repository
import android.graphics.Point
import android.view.MotionEvent
import kotlinx.coroutines.flow.Flow
/**
@@ -24,8 +25,6 @@ import kotlinx.coroutines.flow.Flow
* that talkback is correct.
*/
interface SimulatedTouchEventsRepository {
/**
* A flow simulating user touches.
*/
val touchExplorationDebug: Flow<Point>
/** A flow simulating user touches. */
val touchExplorationDebug: Flow<MotionEvent>
}

View File

@@ -14,10 +14,14 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.data.repository
package com.android.settings.biometrics.fingerprint2.debug.data.repository
import android.graphics.Point
import android.graphics.Rect
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_HOVER_MOVE
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
@@ -36,7 +40,11 @@ import kotlinx.coroutines.flow.flowOf
class UdfpsEnrollDebugRepositoryImpl :
FingerprintEnrollInteractor, FingerprintSensorRepository, SimulatedTouchEventsRepository {
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason) = flow {
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
) = flow {
emit(FingerEnrollState.OverlayShown)
delay(200)
emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world"))
@@ -77,7 +85,7 @@ class UdfpsEnrollDebugRepositoryImpl :
}
/** Provides touch events to the UdfpsEnrollFragment */
override val touchExplorationDebug: Flow<Point> = flow {
override val touchExplorationDebug: Flow<MotionEvent> = flow {
delay(2000)
emit(pointToLeftOfSensor(sensorRect))
delay(2000)
@@ -90,17 +98,45 @@ class UdfpsEnrollDebugRepositoryImpl :
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensorProps)
private fun pointToLeftOfSensor(sensorLocation: Rect) =
Point(sensorLocation.right + 5, sensorLocation.centerY())
private fun pointToLeftOfSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain(
100,
100,
ACTION_HOVER_MOVE,
sensorLocation.right + 5.0f,
sensorLocation.centerY().toFloat(),
0,
)
private fun pointToRightOfSensor(sensorLocation: Rect) =
Point(sensorLocation.left - 5, sensorLocation.centerY())
private fun pointToRightOfSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain(
100,
100,
ACTION_HOVER_MOVE,
sensorLocation.right - 5.0f,
sensorLocation.centerY().toFloat(),
0,
)
private fun pointBelowSensor(sensorLocation: Rect) =
Point(sensorLocation.centerX(), sensorLocation.bottom + 5)
private fun pointBelowSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain(
100,
100,
ACTION_HOVER_MOVE,
sensorLocation.centerX().toFloat(),
sensorLocation.bottom + 5.0f,
0,
)
private fun pointAboveSensor(sensorLocation: Rect) =
Point(sensorLocation.centerX(), sensorLocation.top - 5)
private fun pointAboveSensor(sensorLocation: Rect): MotionEvent =
MotionEvent.obtain(
100,
100,
ACTION_HOVER_MOVE,
sensorLocation.centerX().toFloat(),
sensorLocation.top - 5.0f,
0,
)
companion object {
@@ -109,10 +145,10 @@ class UdfpsEnrollDebugRepositoryImpl :
private val sensorRadius = 100
private val sensorRect =
Rect(
this.sensorLocationInternal.first - sensorRadius,
this.sensorLocationInternal.second - sensorRadius,
this.sensorLocationInternal.first + sensorRadius,
this.sensorLocationInternal.second + sensorRadius,
sensorLocationInternal.first - sensorRadius,
sensorLocationInternal.second - sensorRadius,
sensorLocationInternal.first + sensorRadius,
sensorLocationInternal.second + sensorRadius,
)
val sensorProps =
FingerprintSensor(

View File

@@ -0,0 +1,29 @@
/*
* 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.debug.domain.interactor
import android.view.MotionEvent
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import kotlinx.coroutines.flow.Flow
class DebugTouchEventInteractorImpl(
udfpsSimulatedTouchEventsRepository: SimulatedTouchEventsRepository
) : TouchEventInteractor {
override val touchEvent: Flow<MotionEvent> =
udfpsSimulatedTouchEventsRepository.touchExplorationDebug
}

View File

@@ -17,7 +17,7 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.view.accessibility.AccessibilityManager
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -26,27 +26,27 @@ import kotlinx.coroutines.flow.stateIn
/** Represents all of the information on accessibility state. */
interface AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
val isAccessibilityEnabled: Flow<Boolean>
/** A flow that contains whether or not accessibility is enabled */
val isAccessibilityEnabled: Flow<Boolean>
}
class AccessibilityInteractorImpl(
accessibilityManager: AccessibilityManager,
activityScope: LifecycleCoroutineScope
accessibilityManager: AccessibilityManager,
applicationScope: CoroutineScope,
) : AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
override val isAccessibilityEnabled: Flow<Boolean> =
callbackFlow {
val listener =
AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
accessibilityManager.addAccessibilityStateChangeListener(listener)
// This clause will be called when no one is listening to the flow
awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) }
}
}
.stateIn(
activityScope, // This is going to tied to the activity scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
false
applicationScope, // This is going to tied to the activity scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
false,
)
}

View File

@@ -28,9 +28,7 @@ interface DebuggingInteractor {
val udfpsEnrollmentDebuggingEnabled: Flow<Boolean>
}
/**
* This interactor essentially forwards the [DebuggingRepository]
*/
/** This interactor essentially forwards the [DebuggingRepository] */
class DebuggingInteractorImpl(val debuggingRepository: DebuggingRepository) : DebuggingInteractor {
override val debuggingEnabled: Flow<Boolean> = flow {
emit(debuggingRepository.isDebuggingEnabled())

View File

@@ -16,6 +16,8 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import com.android.settingslib.display.DisplayDensityUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -50,12 +52,11 @@ interface DisplayDensityInteractor {
* Implementation of the [DisplayDensityInteractor]. This interactor is used to forward activity
* information to the rest of the application.
*/
class DisplayDensityInteractorImpl(
currentFontScale: Float,
currentDisplayDensity: Int,
defaultDisplayDensity: Int,
scope: CoroutineScope,
) : DisplayDensityInteractor {
class DisplayDensityInteractorImpl(context: Context, scope: CoroutineScope) :
DisplayDensityInteractor {
val displayDensityUtils = DisplayDensityUtils(context)
override fun updateDisplayDensity(density: Int) {
_displayDensity.update { density }
}
@@ -64,13 +65,18 @@ class DisplayDensityInteractorImpl(
_fontScale.update { fontScale }
}
private val _fontScale = MutableStateFlow(currentFontScale)
private val _displayDensity = MutableStateFlow(currentDisplayDensity)
private val _fontScale = MutableStateFlow(context.resources.configuration.fontScale)
private val _displayDensity =
MutableStateFlow(
displayDensityUtils.defaultDisplayDensityValues[
displayDensityUtils.currentIndexForDefaultDisplay]
)
override val fontScale: Flow<Float> = _fontScale.asStateFlow()
override val displayDensity: Flow<Int> = _displayDensity.asStateFlow()
override val defaultDisplayDensity: Flow<Int> =
flowOf(defaultDisplayDensity).shareIn(scope, SharingStarted.Eagerly, 1)
flowOf(displayDensityUtils.defaultDensityForDefaultDisplay)
.shareIn(scope, SharingStarted.Eagerly, 1)
}

View File

@@ -33,7 +33,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.update
/** This repository is responsible for collecting all state related to the enroll API. */
@@ -45,13 +44,13 @@ interface FingerprintEnrollInteractor {
suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState>
}
class FingerprintEnrollInteractorImpl(
private val applicationContext: Context,
private val fingerprintEnrollOptions: FingerprintEnrollOptions,
private val fingerprintManager: FingerprintManager,
private val fingerprintManager: FingerprintManager?,
private val fingerprintFlow: FingerprintFlow,
) : FingerprintEnrollInteractor {
private val enrollRequestOutstanding = MutableStateFlow(false)
@@ -59,6 +58,7 @@ class FingerprintEnrollInteractorImpl(
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> = callbackFlow {
// TODO (b/308456120) Improve this logic
if (enrollRequestOutstanding.value) {
@@ -135,7 +135,7 @@ class FingerprintEnrollInteractorImpl(
val cancellationSignal = CancellationSignal()
fingerprintManager.enroll(
fingerprintManager?.enroll(
hardwareAuthToken,
cancellationSignal,
applicationContext.userId,

View File

@@ -18,6 +18,7 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.content.Intent
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
@@ -45,7 +46,7 @@ private const val TAG = "FingerprintManagerInteractor"
class FingerprintManagerInteractorImpl(
applicationContext: Context,
private val backgroundDispatcher: CoroutineDispatcher,
private val fingerprintManager: FingerprintManager,
private val fingerprintManager: FingerprintManager?,
fingerprintSensorRepository: FingerprintSensorRepository,
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor,
@@ -57,7 +58,6 @@ class FingerprintManagerInteractorImpl(
)
private val applicationContext = applicationContext.applicationContext
override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> =
suspendCoroutine {
val callback = GenerateChallengeCallback { _, userId, challenge ->
@@ -70,21 +70,19 @@ class FingerprintManagerInteractorImpl(
val p = Pair(challenge, challengeToken)
it.resume(p)
}
fingerprintManager.generateChallenge(applicationContext.userId, callback)
fingerprintManager?.generateChallenge(applicationContext.userId, callback)
}
override val enrolledFingerprints: Flow<List<FingerprintData>> = flow {
override val enrolledFingerprints: Flow<List<FingerprintData>?> = flow {
emit(
fingerprintManager
.getEnrolledFingerprints(applicationContext.userId)
.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }
.toList()
fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)
?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }?.toList()
)
}
override val canEnrollFingerprints: Flow<Boolean> = flow {
emit(
fingerprintManager.getEnrolledFingerprints(applicationContext.userId).size < maxFingerprints
fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)?.size ?: maxFingerprints < maxFingerprints
)
}
@@ -92,8 +90,16 @@ class FingerprintManagerInteractorImpl(
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason): Flow<FingerEnrollState> =
fingerprintEnrollStateRepository.enroll(hardwareAuthToken, enrollReason)
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState> =
fingerprintEnrollStateRepository.enroll(
hardwareAuthToken,
enrollReason,
fingerprintEnrollOptions,
)
override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine {
val callback =
@@ -113,7 +119,7 @@ class FingerprintManagerInteractorImpl(
it.resume(true)
}
}
fingerprintManager.remove(
fingerprintManager?.remove(
android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId),
applicationContext.userId,
callback,
@@ -122,12 +128,12 @@ class FingerprintManagerInteractorImpl(
override suspend fun renameFingerprint(fp: FingerprintData, newName: String) {
withContext(backgroundDispatcher) {
fingerprintManager.rename(fp.fingerId, applicationContext.userId, newName)
fingerprintManager?.rename(fp.fingerId, applicationContext.userId, newName)
}
}
override suspend fun hasSideFps(): Boolean = suspendCancellableCoroutine {
it.resume(fingerprintManager.isPowerbuttonFps)
override suspend fun hasSideFps(): Boolean? = suspendCancellableCoroutine {
it.resume(fingerprintManager?.isPowerbuttonFps)
}
override suspend fun authenticate(): FingerprintAuthAttemptModel =
@@ -156,7 +162,7 @@ class FingerprintManagerInteractorImpl(
val cancellationSignal = CancellationSignal()
c.invokeOnCancellation { cancellationSignal.cancel() }
fingerprintManager.authenticate(
fingerprintManager?.authenticate(
null,
cancellationSignal,
authenticationCallback,
@@ -164,5 +170,4 @@ class FingerprintManagerInteractorImpl(
applicationContext.userId,
)
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.domain.interactor
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import kotlinx.coroutines.flow.Flow
/**
* Interactor that propagates the type of [FingerprintSensor] this device supports.
*/
interface FingerprintSensorInteractor {
/** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor>
}
class FingerprintSensorInteractorImpl(repo: FingerprintSensorRepository) :
FingerprintSensorInteractor {
override val fingerprintSensor: Flow<FingerprintSensor> = repo.fingerprintSensor
}

View File

@@ -25,34 +25,30 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
interface FoldStateInteractor {
/** A flow that contains the fold state info */
val isFolded: Flow<Boolean>
/** A flow that contains the fold state info */
val isFolded: Flow<Boolean>
/**
* Indicates a configuration change has occurred, and the repo
* should update the [isFolded] flow.
*/
fun onConfigurationChange(newConfig: Configuration)
/**
* Indicates a configuration change has occurred, and the repo should update the [isFolded] flow.
*/
fun onConfigurationChange(newConfig: Configuration)
}
/**
* Interactor which handles fold state
*/
/** Interactor which handles fold state */
class FoldStateInteractorImpl(context: Context) : FoldStateInteractor {
private val screenSizeFoldProvider = ScreenSizeFoldProvider(context)
override val isFolded: Flow<Boolean> = callbackFlow {
val foldStateListener = FoldProvider.FoldCallback { isFolded -> trySend(isFolded) }
screenSizeFoldProvider.registerCallback(foldStateListener, context.mainExecutor)
awaitClose { screenSizeFoldProvider.unregisterCallback(foldStateListener) }
}
/**
* This function is called by the root activity, indicating an orientation event has occurred.
* When this happens, the [ScreenSizeFoldProvider] is notified and it will re-compute if the
* device is folded or not, and notify the [FoldProvider.FoldCallback]
*/
override fun onConfigurationChange(newConfig: Configuration) {
screenSizeFoldProvider.onConfigurationChange(newConfig)
}
private val screenSizeFoldProvider = ScreenSizeFoldProvider(context)
override val isFolded: Flow<Boolean> = callbackFlow {
val foldStateListener = FoldProvider.FoldCallback { isFolded -> trySend(isFolded) }
screenSizeFoldProvider.registerCallback(foldStateListener, context.mainExecutor)
awaitClose { screenSizeFoldProvider.unregisterCallback(foldStateListener) }
}
/**
* This function is called by the root activity, indicating an orientation event has occurred.
* When this happens, the [ScreenSizeFoldProvider] is notified and it will re-compute if the
* device is folded or not, and notify the [FoldProvider.FoldCallback]
*/
override fun onConfigurationChange(newConfig: Configuration) {
screenSizeFoldProvider.onConfigurationChange(newConfig)
}
}

View File

@@ -60,10 +60,7 @@ class OrientationInteractorImpl(private val context: Context) : OrientationInter
awaitClose { orientationEventListener.disable() }
}
override val rotation: Flow<Int> =
orientation.transform {
emit(context.display!!.rotation)
}
override val rotation: Flow<Int> = orientation.transform { emit(context.display.rotation) }
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }

View File

@@ -28,77 +28,67 @@ import kotlinx.coroutines.flow.flowOn
/** Interface that indicates if press to auth is on or off. */
interface PressToAuthInteractor {
/** Indicates true if the PressToAuth feature is enabled, false otherwise. */
val isEnabled: Flow<Boolean>
/** Indicates true if the PressToAuth feature is enabled, false otherwise. */
val isEnabled: Flow<Boolean>
}
/** Indicates whether or not the press to auth feature is enabled. */
class PressToAuthInteractorImpl(
private val context: Context,
private val backgroundDispatcher: CoroutineDispatcher,
private val context: Context,
private val backgroundDispatcher: CoroutineDispatcher,
) : PressToAuthInteractor {
/**
* A flow that contains the status of the press to auth feature.
*/
override val isEnabled: Flow<Boolean> =
callbackFlow {
val callback =
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
Log.d(TAG, "SFPS_PERFORMANT_AUTH_ENABLED#onchange")
trySend(
getPressToAuth(),
)
}
}
context.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED),
false,
callback,
context.userId
)
trySend(getPressToAuth())
awaitClose {
context.contentResolver.unregisterContentObserver(callback)
/** A flow that contains the status of the press to auth feature. */
override val isEnabled: Flow<Boolean> =
callbackFlow {
val callback =
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
Log.d(TAG, "SFPS_PERFORMANT_AUTH_ENABLED#onchange")
trySend(getPressToAuth())
}
}.flowOn(backgroundDispatcher)
}
context.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED),
false,
callback,
context.userId,
)
trySend(getPressToAuth())
awaitClose { context.contentResolver.unregisterContentObserver(callback) }
}
.flowOn(backgroundDispatcher)
/**
* Returns true if press to auth is enabled
*/
private fun getPressToAuth(): Boolean {
var toReturn: Int =
Settings.Secure.getIntForUser(
context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
-1,
context.userId,
)
if (toReturn == -1) {
toReturn =
if (
context.resources.getBoolean(com.android.internal.R.bool.config_performantAuthDefault)
) {
1
} else {
0
}
Settings.Secure.putIntForUser(
context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn,
context.userId,
)
/** Returns true if press to auth is enabled */
private fun getPressToAuth(): Boolean {
var toReturn: Int =
Settings.Secure.getIntForUser(
context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
-1,
context.userId,
)
if (toReturn == -1) {
toReturn =
if (
context.resources.getBoolean(com.android.internal.R.bool.config_performantAuthDefault)
) {
1
} else {
0
}
return toReturn == 1
Settings.Secure.putIntForUser(
context.contentResolver,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
toReturn,
context.userId,
)
}
return toReturn == 1
}
companion object {
const val TAG = "PressToAuthInteractor"
}
companion object {
const val TAG = "PressToAuthInteractor"
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.domain.interactor
import android.view.MotionEvent
import kotlinx.coroutines.flow.Flow
interface TouchEventInteractor {
/** A flow simulating user touches. */
val touchEvent: Flow<MotionEvent>
}

View File

@@ -16,8 +16,9 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.graphics.PointF
import android.util.Log
import android.util.TypedValue
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -43,13 +44,18 @@ interface UdfpsEnrollInteractor {
/** Keeps track of which guided enrollment point we should be using */
class UdfpsEnrollInteractorImpl(
pixelsPerMillimeter: Float,
applicationContext: Context,
accessibilityInteractor: AccessibilityInteractor,
) : UdfpsEnrollInteractor {
private var isGuidedEnrollment = MutableStateFlow(false)
// Number of pixels per mm
val px = pixelsPerMillimeter
val px =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_MM,
1f,
applicationContext.resources.displayMetrics,
)
private val guidedEnrollmentPoints: MutableList<PointF> =
mutableListOf(
PointF(2.00f * px, 0.00f * px),
@@ -70,7 +76,6 @@ class UdfpsEnrollInteractorImpl(
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
Log.e("JRM", "guided enroll step $index")
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
}

View File

@@ -36,6 +36,7 @@ sealed class FingerprintVibrationEffects {
/** This vibration typically occurs when a help message is shown during UDFPS enrollment */
data object UdfpsHelp : FingerprintVibrationEffects()
}
/** Interface for sending haptic feedback */
interface VibrationInteractor {
/** This will send a haptic vibration */
@@ -43,8 +44,9 @@ interface VibrationInteractor {
}
/** Implementation of the VibrationInteractor interface */
class VibrationInteractorImpl(val vibrator: Vibrator, val applicationContext: Context) :
VibrationInteractor {
class VibrationInteractorImpl(val applicationContext: Context) : VibrationInteractor {
val vibrator = applicationContext.getSystemService(Vibrator::class.java)!!
override fun vibrate(effect: FingerprintVibrationEffects, caller: String) {
val callerString = "$caller::$effect"
val res =

View File

@@ -16,7 +16,7 @@
package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
@@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.Flow
*/
interface FingerprintManagerInteractor {
/** Returns the list of current fingerprints. */
val enrolledFingerprints: Flow<List<FingerprintData>>
val enrolledFingerprints: Flow<List<FingerprintData>?>
/** Returns the max enrollable fingerprints, note during SUW this might be 1 */
val maxEnrollableFingerprints: Flow<Int>
@@ -62,6 +62,7 @@ interface FingerprintManagerInteractor {
suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions,
): Flow<FingerEnrollState>
/**
@@ -74,5 +75,5 @@ interface FingerprintManagerInteractor {
suspend fun renameFingerprint(fp: FingerprintData, newName: String)
/** Indicates if the device has side fingerprint */
suspend fun hasSideFps(): Boolean
suspend fun hasSideFps(): Boolean?
}

View File

@@ -24,5 +24,5 @@ enum class EnrollReason {
*/
FindSensor,
/** The enroll happens on enrolling screen. */
EnrollEnrolling
EnrollEnrolling,
}

View File

@@ -22,16 +22,12 @@ import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintManager
import android.os.Bundle
import android.os.Vibrator
import android.util.Log
import android.util.TypedValue
import android.view.accessibility.AccessibilityManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.internal.widget.LockPatternUtils
import com.android.settings.R
import com.android.settings.SetupWizardUtils
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
@@ -40,27 +36,9 @@ import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.BiometricUtils
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.data.repository.UdfpsEnrollDebugRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
@@ -68,18 +46,12 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.SfpsEnrollFindSensorFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.UdfpsEnrollFindSensorFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
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.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
@@ -91,7 +63,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
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.GatekeeperInfo
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Transition
import com.android.settings.flags.Flags
@@ -113,23 +84,38 @@ private const val TAG = "FingerprintEnrollmentV2Activity"
* children fragments.
*/
class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
private lateinit var vibrationInteractor: VibrationInteractor
private val navigationViewModel: FingerprintNavigationViewModel by viewModels {
FingerprintNavigationViewModel.Factory
}
private val fingerprintFlowViewModel: FingerprintFlowViewModel by viewModels {
FingerprintFlowViewModel.Factory
}
private val gatekeeperViewModel: FingerprintGatekeeperViewModel by viewModels {
FingerprintGatekeeperViewModel.Factory
}
/**
* View models below this line are not used by this class but must be initialized
* in the activity view model store to be used by other view models.
*/
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel by viewModels {
FingerprintEnrollViewModel.Factory
}
private val fingerprintEnrollEnrollingViewModel:
FingerprintEnrollEnrollingViewModel by viewModels {
FingerprintEnrollEnrollingViewModel.Factory
}
private val udfpsLastStepViewModel: UdfpsLastStepViewModel by viewModels {
UdfpsLastStepViewModel.Factory
}
private val backgroundViewModel: BackgroundViewModel by viewModels { BackgroundViewModel.Factory }
private lateinit var foldStateInteractor: FoldStateInteractor
private lateinit var orientationInteractor: OrientationInteractor
private lateinit var displayDensityInteractor: DisplayDensityInteractor
private lateinit var udfpsEnrollInteractor: UdfpsEnrollInteractor
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
private lateinit var fingerprintEnrollConfirmationViewModel:
FingerprintEnrollConfirmationViewModel
private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel
private lateinit var udfpsViewModel: UdfpsViewModel
private lateinit var enrollStageInteractor: EnrollStageInteractor
private val coroutineDispatcher = Dispatchers.Default
/** Result listener for ChooseLock activity flow. */
@@ -172,7 +158,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK
val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long?
val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long
lifecycleScope.launch {
val confirmDeviceResult =
@@ -204,6 +190,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
finish()
}
// Ensure that these view models are actually created and in this order
navigationViewModel
fingerprintFlowViewModel
gatekeeperViewModel
fingerprintEnrollViewModel
backgroundViewModel
fingerprintEnrollEnrollingViewModel
udfpsLastStepViewModel
setTheme(SetupWizardUtils.getTheme(applicationContext, intent))
ThemeHelper.trySetDynamicColor(applicationContext)
@@ -219,31 +214,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
Default
}
backgroundViewModel =
ViewModelProvider(this, BackgroundViewModel.BackgroundViewModelFactory())[
BackgroundViewModel::class.java]
fingerprintFlowViewModel =
ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[
FingerprintFlowViewModel::class.java]
val displayDensityUtils = DisplayDensityUtils(context)
val currIndex = displayDensityUtils.currentIndexForDefaultDisplay
val defaultDisplayDensity = displayDensityUtils.defaultDensityForDefaultDisplay
displayDensityInteractor =
DisplayDensityInteractorImpl(
resources.configuration.fontScale,
displayDensityUtils.defaultDisplayDensityValues[currIndex],
defaultDisplayDensity,
lifecycleScope,
)
val debuggingRepo = DebuggingRepositoryImpl()
val debuggingInteractor = DebuggingInteractorImpl(debuggingRepo)
val udfpsEnrollDebugRepositoryImpl = UdfpsEnrollDebugRepositoryImpl()
val fingerprintSensorRepo =
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
else FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
fingerprintFlowViewModel.updateFlowType(enrollType)
if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) {
val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent)
@@ -254,170 +225,18 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
)
}
val fingerprintEnrollStateRepository =
if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl
else
FingerprintEnrollInteractorImpl(
context.applicationContext,
intent.toFingerprintEnrollOptions(),
fingerprintManager,
Settings,
)
val accessibilityInteractor =
AccessibilityInteractorImpl(
getSystemService(AccessibilityManager::class.java)!!,
lifecycleScope,
)
val pixelsPerMillimeter =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, context.resources.displayMetrics)
udfpsEnrollInteractor = UdfpsEnrollInteractorImpl(pixelsPerMillimeter, accessibilityInteractor)
val fingerprintManagerInteractor =
FingerprintManagerInteractorImpl(
context,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorRepo,
GatekeeperPasswordProvider(LockPatternUtils(context)),
fingerprintEnrollStateRepository,
)
var challenge = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long?
val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token)
val hasConfirmedDeviceCredential = gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo
navigationViewModel =
ViewModelProvider(
this,
FingerprintNavigationViewModel.FingerprintNavigationViewModelFactory(
Init,
hasConfirmedDeviceCredential,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintNavigationViewModel::class.java]
// Initialize FingerprintEnrollIntroViewModel
ViewModelProvider(
this,
FingerprintEnrollIntroViewModel.FingerprintEnrollIntoViewModelFactory(
navigationViewModel,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintEnrollIntroViewModel::class.java]
gatekeeperViewModel =
ViewModelProvider(
this,
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
gatekeeperInfo,
fingerprintManagerInteractor,
),
)[FingerprintGatekeeperViewModel::class.java]
// Initialize FoldStateViewModel
foldStateInteractor = FoldStateInteractorImpl(context)
foldStateInteractor.onConfigurationChange(resources.configuration)
orientationInteractor = OrientationInteractorImpl(context)
vibrationInteractor =
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
// Initialize FingerprintViewModel
fingerprintEnrollViewModel =
ViewModelProvider(
this,
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
fingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
),
)[FingerprintEnrollViewModel::class.java]
// Initialize scroll view model
fingerprintScrollViewModel =
ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
FingerprintScrollViewModel::class.java]
// Initialize FingerprintEnrollEnrollingViewModel
fingerprintEnrollEnrollingViewModel =
ViewModelProvider(
this,
FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory(
fingerprintEnrollViewModel,
backgroundViewModel,
),
)[FingerprintEnrollEnrollingViewModel::class.java]
// Initialize FingerprintEnrollFindSensorViewModel
ViewModelProvider(
this,
FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
navigationViewModel,
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintEnrollFindSensorViewModel::class.java]
// Initialize RFPS View Model
ViewModelProvider(
this,
RFPSViewModel.RFPSViewModelFactory(
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
fingerprintManagerInteractor,
),
)[RFPSViewModel::class.java]
enrollStageInteractor = EnrollStageInteractorImpl()
udfpsLastStepViewModel =
UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor)
udfpsViewModel =
ViewModelProvider(
this,
UdfpsViewModel.UdfpsEnrollmentFactory(
vibrationInteractor,
displayDensityInteractor,
navigationViewModel,
debuggingInteractor,
fingerprintEnrollEnrollingViewModel,
udfpsEnrollDebugRepositoryImpl,
enrollStageInteractor,
orientationInteractor,
backgroundViewModel,
fingerprintSensorRepo,
udfpsEnrollInteractor,
fingerprintManagerInteractor,
udfpsLastStepViewModel,
accessibilityInteractor,
),
)[UdfpsViewModel::class.java]
fingerprintEnrollConfirmationViewModel =
ViewModelProvider(
this,
FingerprintEnrollConfirmationViewModel.FingerprintEnrollConfirmationViewModelFactory(
navigationViewModel,
fingerprintManagerInteractor,
),
)[FingerprintEnrollConfirmationViewModel::class.java]
navigationViewModel.updateFingerprintFlow(enrollType)
navigationViewModel.hasConfirmedDeviceCredential(hasConfirmedDeviceCredential)
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")
}
}

View File

@@ -20,8 +20,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@@ -39,31 +39,11 @@ import kotlinx.coroutines.launch
* This page will display basic information about what a fingerprint can be used for and acts as the
* final step of enrollment.
*/
class FingerprintEnrollConfirmationV2Fragment() :
class FingerprintEnrollConfirmationV2Fragment(factory: ViewModelProvider.Factory? = null) :
Fragment(R.layout.fingerprint_enroll_finish_base) {
companion object {
const val TAG = "FingerprintEnrollConfirmationV2Fragment"
}
/** Used for testing purposes */
private var factory: ViewModelProvider.Factory? = null
@VisibleForTesting
constructor(theFactory: ViewModelProvider.Factory) : this() {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private val viewModel: FingerprintEnrollConfirmationViewModel by lazy {
viewModelProvider[FingerprintEnrollConfirmationViewModel::class.java]
private val viewModel: FingerprintEnrollConfirmationViewModel by activityViewModels {
factory ?: FingerprintEnrollConfirmationViewModel.Factory
}
override fun onCreateView(
@@ -106,4 +86,8 @@ class FingerprintEnrollConfirmationV2Fragment() :
private fun onNextButtonClick(view: View?) {
viewModel.onNextButtonClicked()
}
companion object {
const val TAG = "FingerprintEnrollConfirmationV2Fragment"
}
}

View File

@@ -30,8 +30,9 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ScrollView
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.settings.R
@@ -74,37 +75,22 @@ private data class TextModel(
* 2. How the data will be stored
* 3. How the user can access and remove their data
*/
class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
/** Used for testing purposes */
private var factory: ViewModelProvider.Factory? = null
@VisibleForTesting
constructor(theFactory: ViewModelProvider.Factory) : this() {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
class FingerprintEnrollIntroV2Fragment(testFactory: ViewModelProvider.Factory? = null) :
Fragment(R.layout.fingerprint_v2_enroll_introduction) {
private lateinit var footerBarMixin: FooterBarMixin
private lateinit var textModel: TextModel
private val viewModel: FingerprintEnrollIntroViewModel by lazy {
viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
private val viewModel: FingerprintEnrollIntroViewModel by activityViewModels {
testFactory ?: FingerprintEnrollIntroViewModel.Factory
}
private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy {
viewModelProvider[FingerprintScrollViewModel::class.java]
private val fingerprintScrollViewModel: FingerprintScrollViewModel by viewModels {
testFactory ?: FingerprintScrollViewModel.Factory
}
private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy {
viewModelProvider[FingerprintGatekeeperViewModel::class.java]
private val gateKeeperViewModel: FingerprintGatekeeperViewModel by activityViewModels {
testFactory ?: FingerprintGatekeeperViewModel.Factory
}
override fun onCreateView(

View File

@@ -23,11 +23,15 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
@@ -51,18 +55,10 @@ class RfpsEnrollFindSensorFragment() : Fragment() {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private var animation: FingerprintFindSensorAnimation? = null
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels {
factory ?: FingerprintEnrollFindSensorViewModel.Factory
}
override fun onCreateView(
@@ -78,6 +74,12 @@ class RfpsEnrollFindSensorFragment() : Fragment() {
// Set up footer bar
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
setupSecondaryButton(footerBarMixin)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions())
}
}
lifecycleScope.launch {
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
}

View File

@@ -24,11 +24,15 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
@@ -52,16 +56,8 @@ class SfpsEnrollFindSensorFragment() : Fragment() {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels {
factory ?: FingerprintEnrollFindSensorViewModel.Factory
}
override fun onCreateView(
@@ -78,6 +74,12 @@ class SfpsEnrollFindSensorFragment() : Fragment() {
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
setupSecondaryButton(footerBarMixin)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions())
}
}
// Set up lottie
lifecycleScope.launch {
viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->

View File

@@ -24,11 +24,15 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
@@ -53,16 +57,8 @@ class UdfpsEnrollFindSensorFragment() : Fragment() {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels {
factory ?: FingerprintEnrollFindSensorViewModel.Factory
}
override fun onCreateView(
@@ -79,6 +75,12 @@ class UdfpsEnrollFindSensorFragment() : Fragment() {
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
setupSecondaryButton(footerBarMixin)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions())
}
}
lifecycleScope.launch {
viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
}

View File

@@ -28,12 +28,15 @@ import android.view.animation.Interpolator
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
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
@@ -62,29 +65,21 @@ class RFPSEnrollFragment() : Fragment(R.layout.fingerprint_v2_rfps_enroll_enroll
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private lateinit var linearOutSlowInInterpolator: Interpolator
private lateinit var fastOutLinearInInterpolator: Interpolator
private lateinit var textView: TextView
private lateinit var progressBar: RFPSProgressBar
private val iconTouchViewModel: RFPSIconTouchViewModel by lazy {
viewModelProvider[RFPSIconTouchViewModel::class.java]
private val iconTouchViewModel: RFPSIconTouchViewModel by viewModels {
RFPSIconTouchViewModel.Factory
}
private val rfpsViewModel: RFPSViewModel by lazy { viewModelProvider[RFPSViewModel::class.java] }
private val backgroundViewModel: BackgroundViewModel by lazy {
viewModelProvider[BackgroundViewModel::class.java]
private val rfpsViewModel: RFPSViewModel by activityViewModels {
factory ?: RFPSViewModel.Factory
}
private val backgroundViewModel: BackgroundViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -131,6 +126,7 @@ class RFPSEnrollFragment() : Fragment(R.layout.fingerprint_v2_rfps_enroll_enroll
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
rfpsViewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions())
// Icon animation update
viewLifecycleOwner.lifecycleScope.launch {
// TODO(b/324427704): Fix this delay

View File

@@ -19,6 +19,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -27,6 +29,7 @@ import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.flow.update
private const val touchesToShowDialog = 3
/**
* This class is responsible for counting the number of touches on the fingerprint icon, and if this
* number reaches a threshold it will produce an action via [shouldShowDialog] to indicate the ui
@@ -52,10 +55,9 @@ class RFPSIconTouchViewModel : ViewModel() {
_touches.update { _touches.value + 1 }
}
class RFPSIconTouchViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RFPSIconTouchViewModel() as T
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { RFPSIconTouchViewModel() }
}
}
}

View File

@@ -16,9 +16,14 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel
import android.hardware.fingerprint.FingerprintEnrollOptions
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
@@ -158,27 +163,27 @@ class RFPSViewModel(
enrollFlow = fingerprintEnrollViewModel.enrollFlow
}
class RFPSViewModelFactory(
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
private val orientationInteractor: OrientationInteractor,
private val fingerprintManager: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RFPSViewModel(
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
fingerprintManager,
)
as T
}
/** Starts enrollment. */
fun enroll(enrollOptions: FingerprintEnrollOptions) {
fingerprintEnrollViewModel.enroll(enrollOptions)
}
companion object {
private val navStep = Enrollment::class
private const val TAG = "RFPSViewModel"
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment!!
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
RFPSViewModel(
provider[FingerprintEnrollEnrollingViewModel::class],
provider[FingerprintNavigationViewModel::class],
biometricEnvironment.orientationInteractor,
biometricEnvironment.fingerprintManagerInteractor,
)
}
}
}
}

View File

@@ -82,6 +82,7 @@ class RFPSProgressBar : RingProgressBar {
shouldAnimateInternal = shouldAnimate
}
/** This function should only be called when actual progress has been made. */
fun updateProgress(percentComplete: Float) {
val progress = maxProgress - (percentComplete.coerceIn(0.0f, 100.0f) * maxProgress).toInt()

View File

@@ -18,8 +18,6 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_HOVER_MOVE
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -27,6 +25,7 @@ import android.widget.Button
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@@ -36,6 +35,7 @@ import com.airbnb.lottie.LottieCompositionFactory
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
@@ -50,17 +50,10 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
/** Used for testing purposes */
private var factory: ViewModelProvider.Factory? = null
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
private lateinit var lottie: LottieAnimationView
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
private val viewModel: UdfpsViewModel by activityViewModels { factory ?: UdfpsViewModel.Factory }
@VisibleForTesting
constructor(theFactory: ViewModelProvider.Factory) : this() {
@@ -90,6 +83,7 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions())
launch {
viewModel.sensorLocation.collect { sensor ->
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
@@ -204,12 +198,16 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.touchExplorationDebug.collect {
udfpsEnrollView.sendDebugTouchExplorationEvent(
MotionEvent.obtain(100, 100, ACTION_HOVER_MOVE, it.x.toFloat(), it.y.toFloat(), 0)
)
view.setOnTouchListener { _, motionEvent ->
viewModel.onTouchEvent(motionEvent)
false
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.touchEvent.collect { udfpsEnrollView.onTouchEvent(it) }
}
viewModel.readyForEnrollment()
}

View File

@@ -16,9 +16,13 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
@@ -89,14 +93,18 @@ class UdfpsLastStepViewModel(
}
.filterNotNull()
class UdfpsLastStepViewModelFactory(
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val vibrationInteractor: VibrationInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) as T
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
UdfpsLastStepViewModel(
provider[FingerprintEnrollEnrollingViewModel::class],
biometricEnvironment!!.vibrationInteractor,
)
}
}
}
}

View File

@@ -16,21 +16,26 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
import android.graphics.Point
import android.graphics.PointF
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.view.MotionEvent
import android.view.Surface
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
@@ -45,6 +50,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -60,20 +66,20 @@ import kotlinx.coroutines.launch
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
class UdfpsViewModel(
val navigationViewModel: FingerprintNavigationViewModel,
val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
backgroundViewModel: BackgroundViewModel,
val udfpsLastStepViewModel: UdfpsLastStepViewModel,
val vibrationInteractor: VibrationInteractor,
displayDensityInteractor: DisplayDensityInteractor,
val navigationViewModel: FingerprintNavigationViewModel,
debuggingInteractor: DebuggingInteractor,
val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
simulatedTouchEventsDebugRepository: SimulatedTouchEventsRepository,
enrollStageInteractor: EnrollStageInteractor,
orientationInteractor: OrientationInteractor,
backgroundViewModel: BackgroundViewModel,
sensorRepository: FingerprintSensorRepository,
udfpsEnrollInteractor: UdfpsEnrollInteractor,
fingerprintManager: FingerprintManagerInteractor,
val udfpsLastStepViewModel: UdfpsLastStepViewModel,
accessibilityInteractor: AccessibilityInteractor,
sensorRepository: FingerprintSensorInteractor,
touchEventInteractor: TouchEventInteractor,
) : ViewModel() {
private val isSetupWizard = flowOf(false)
@@ -191,15 +197,9 @@ class UdfpsViewModel(
.transform { emit(it == Surface.ROTATION_90) }
.distinctUntilChanged()
/** This sends touch exploration events only used for debugging purposes. */
val touchExplorationDebug: Flow<Point> =
debuggingInteractor.debuggingEnabled.combineTransform(
simulatedTouchEventsDebugRepository.touchExplorationDebug
) { enabled, point ->
if (enabled) {
emit(point)
}
}
private val _touchEvent: MutableStateFlow<MotionEvent?> = MutableStateFlow(null)
val touchEvent =
_touchEvent.asStateFlow().filterNotNull()
/** Determines the current [EnrollStageModel] enrollment is in */
private val enrollStage: Flow<EnrollStageModel> =
@@ -266,6 +266,12 @@ class UdfpsViewModel(
viewModelScope.launch {
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
}
viewModelScope.launch {
touchEventInteractor.touchEvent.collect {
_touchEvent.update { it }
}
}
}
/** Indicates if we should show the lottie. */
@@ -393,47 +399,43 @@ class UdfpsViewModel(
)
}
class UdfpsEnrollmentFactory(
private val vibrationInteractor: VibrationInteractor,
private val displayDensityInteractor: DisplayDensityInteractor,
private val navigationViewModel: FingerprintNavigationViewModel,
private val debuggingInteractor: DebuggingInteractor,
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val simulatedTouchEventsRepository: SimulatedTouchEventsRepository,
private val enrollStageInteractor: EnrollStageInteractor,
private val orientationInteractor: OrientationInteractor,
private val backgroundViewModel: BackgroundViewModel,
private val sensorRepository: FingerprintSensorRepository,
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
private val fingerprintManager: FingerprintManagerInteractor,
private val udfpsLastStepViewModel: UdfpsLastStepViewModel,
private val accessibilityInteractor: AccessibilityInteractor,
) : ViewModelProvider.Factory {
/** Starts enrollment. */
fun enroll(enrollOptions: FingerprintEnrollOptions) {
fingerprintEnrollEnrollingViewModel.enroll(enrollOptions)
}
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UdfpsViewModel(
vibrationInteractor,
displayDensityInteractor,
navigationViewModel,
debuggingInteractor,
fingerprintEnrollEnrollingViewModel,
simulatedTouchEventsRepository,
enrollStageInteractor,
orientationInteractor,
backgroundViewModel,
sensorRepository,
udfpsEnrollInteractor,
fingerprintManager,
udfpsLastStepViewModel,
accessibilityInteractor,
)
as T
}
/** Indicates a touch event has occurred. */
fun onTouchEvent(event: MotionEvent) {
_touchEvent.update { event }
}
companion object {
private val navStep = FingerprintNavigationStep.Enrollment::class
private const val TAG = "UDFPSViewModel"
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment!!
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
UdfpsViewModel(
provider[FingerprintNavigationViewModel::class],
provider[FingerprintEnrollEnrollingViewModel::class],
provider[BackgroundViewModel::class],
provider[UdfpsLastStepViewModel::class],
biometricEnvironment.vibrationInteractor,
biometricEnvironment.displayDensityInteractor,
biometricEnvironment.debuggingInteractor,
biometricEnvironment.enrollStageInteractor,
biometricEnvironment.orientationInteractor,
biometricEnvironment.udfpsEnrollInteractor,
biometricEnvironment.fingerprintManagerInteractor,
biometricEnvironment.accessibilityInteractor,
biometricEnvironment.sensorInteractor,
biometricEnvironment.touchEventInteractor,
)
}
}
}
}

View File

@@ -210,6 +210,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
override fun getOpacity(): Int {
return PixelFormat.UNKNOWN
}
/**
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
* respect to the parent framelayout.

View File

@@ -148,18 +148,10 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
}
}
/**
* Sends a touch exploration event to the [onHoverListener] this should only be used for
* debugging.
*/
fun sendDebugTouchExplorationEvent(motionEvent: MotionEvent) {
touchExplorationAnnouncer.onTouch(motionEvent)
}
/** Sets the addHoverListener, this should happen when talkback is enabled. */
private fun addHoverListener() {
onHoverListener = OnHoverListener { _: View, event: MotionEvent ->
sendDebugTouchExplorationEvent(event)
touchExplorationAnnouncer.onTouch(event)
false
}
this.setOnHoverListener(onHoverListener)

View File

@@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@@ -39,10 +41,9 @@ class BackgroundViewModel : ViewModel() {
_background.update { false }
}
class BackgroundViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return BackgroundViewModel() as T
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { BackgroundViewModel() }
}
}
}

View File

@@ -16,14 +16,17 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.util.Log
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import kotlinx.coroutines.flow.Flow
/**
* Models the UI state for [FingerprintEnrollConfirmationV2Fragment]
*/
/** Models the UI state for [FingerprintEnrollConfirmationV2Fragment] */
class FingerprintEnrollConfirmationViewModel(
private val navigationViewModel: FingerprintNavigationViewModel,
fingerprintInteractor: FingerprintManagerInteractor,
@@ -50,18 +53,20 @@ class FingerprintEnrollConfirmationViewModel(
navigationViewModel.update(FingerprintAction.ADD_ANOTHER, navStep, "onAddAnotherButtonClicked")
}
class FingerprintEnrollConfirmationViewModelFactory(
private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintInteractor) as T
}
}
companion object {
private const val TAG = "FingerprintEnrollConfirmationViewModel"
private val navStep = FingerprintNavigationStep.Confirmation::class
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollConfirmationViewModel(
provider[FingerprintNavigationViewModel::class],
biometricEnvironment!!.fingerprintManagerInteractor,
)
}
}
}
}

View File

@@ -16,14 +16,15 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.util.Log
import android.hardware.fingerprint.FingerprintEnrollOptions
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
@@ -69,23 +70,24 @@ class FingerprintEnrollEnrollingViewModel(
val enrollFlow =
enrollFlowShouldBeRunning.transformLatest {
if (it) {
fingerprintEnrollViewModel.enrollFlow.collect { event ->
emit(event) }
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
}
}
class FingerprintEnrollEnrollingViewModelFactory(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val backgroundViewModel: BackgroundViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
as T
}
/** Indicates enrollment to start */
fun enroll(enrollOptions: FingerprintEnrollOptions) {
fingerprintEnrollViewModel.enroll(enrollOptions)
}
companion object {
private val TAG = "FingerprintEnrollEnrollingViewModel"
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollEnrollingViewModel(
provider[FingerprintEnrollViewModel::class],
provider[BackgroundViewModel::class],
)
}
}
}
}

View File

@@ -16,9 +16,14 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.hardware.fingerprint.FingerprintEnrollOptions
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
@@ -43,10 +48,10 @@ class FingerprintEnrollFindSensorViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
backgroundViewModel: BackgroundViewModel,
fingerprintFlowViewModel: FingerprintFlowViewModel,
accessibilityInteractor: AccessibilityInteractor,
foldStateInteractor: FoldStateInteractor,
orientationInteractor: OrientationInteractor,
fingerprintFlowViewModel: FingerprintFlowViewModel,
fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModel() {
@@ -64,6 +69,7 @@ class FingerprintEnrollFindSensorViewModel(
val showPrimaryButton: Flow<Boolean> = _isUdfps.filter { it }
private val _showSfpsLottie = _isSfps.filter { it }
/** Represents the stream of showing sfps lottie and the information Pair(isFolded, rotation). */
val sfpsLottieInfo: Flow<Pair<Boolean, Int>> =
combineTransform(
@@ -75,6 +81,7 @@ class FingerprintEnrollFindSensorViewModel(
}
private val _showUdfpsLottie = _isUdfps.filter { it }
/** Represents the stream of showing udfps lottie and whether accessibility is enabled. */
val udfpsLottieInfo: Flow<Boolean> =
_showUdfpsLottie.combine(accessibilityInteractor.isAccessibilityEnabled) {
@@ -87,11 +94,13 @@ class FingerprintEnrollFindSensorViewModel(
val showRfpsAnimation: Flow<Boolean> = _isRearSfps.filter { it }
private val _showErrorDialog: MutableStateFlow<Pair<Int, Boolean>?> = MutableStateFlow(null)
/** Represents the stream of showing error dialog. */
val showErrorDialog = _showErrorDialog.filterNotNull()
private var _didTryEducation = false
private var _education: MutableStateFlow<Boolean> = MutableStateFlow(false)
/** Indicates if the education flow should be running. */
private val educationFlowShouldBeRunning: Flow<Boolean> =
_education.combine(backgroundViewModel.background) { shouldRunEducation, isInBackground ->
@@ -167,6 +176,11 @@ class FingerprintEnrollFindSensorViewModel(
_education.update { false }
}
/** Indicates enrollment to start */
fun enroll(enrollOptions: FingerprintEnrollOptions) {
fingerprintEnrollViewModel.enroll(enrollOptions)
}
/** Proceed to EnrollEnrolling page. */
fun proceedToEnrolling() {
stopEducation()
@@ -182,36 +196,29 @@ class FingerprintEnrollFindSensorViewModel(
)
}
class FingerprintEnrollFindSensorViewModelFactory(
private val navigationViewModel: FingerprintNavigationViewModel,
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
private val backgroundViewModel: BackgroundViewModel,
private val accessibilityInteractor: AccessibilityInteractor,
private val foldStateInteractor: FoldStateInteractor,
private val orientationInteractor: OrientationInteractor,
private val fingerprintFlowViewModel: FingerprintFlowViewModel,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollFindSensorViewModel(
navigationViewModel,
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
)
as T
}
}
companion object {
private const val TAG = "FingerprintEnrollFindSensorViewModel"
private val navStep = Education::class
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment!!
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollFindSensorViewModel(
provider[FingerprintNavigationViewModel::class],
provider[FingerprintEnrollViewModel::class],
provider[FingerprintGatekeeperViewModel::class],
provider[BackgroundViewModel::class],
provider[FingerprintFlowViewModel::class],
biometricEnvironment.accessibilityInteractor,
biometricEnvironment.foldStateInteractor,
biometricEnvironment.orientationInteractor,
biometricEnvironment.fingerprintManagerInteractor,
)
}
}
}
}

View File

@@ -16,8 +16,12 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
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
@@ -27,8 +31,8 @@ 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,
fingerprintFlowViewModel: FingerprintFlowViewModel,
fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModel() {
/** Represents a stream of [FingerprintSensor] */
@@ -51,25 +55,21 @@ class FingerprintEnrollIntroViewModel(
)
}
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"
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollIntroViewModel(
provider[FingerprintNavigationViewModel::class],
provider[FingerprintFlowViewModel::class],
biometricEnvironment!!.fingerprintManagerInteractor,
)
}
}
}
}

View File

@@ -15,9 +15,15 @@
*/
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.hardware.fingerprint.FingerprintEnrollOptions
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
@@ -25,12 +31,14 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.flow.update
/** Represents all of the fingerprint information needed for a fingerprint enrollment process. */
class FingerprintEnrollViewModel(
@@ -55,6 +63,8 @@ class FingerprintEnrollViewModel(
}
}
private val enrollOptions: MutableStateFlow<FingerprintEnrollOptions?> = MutableStateFlow(null)
/** Represents the stream of [FingerprintSensorType] */
val sensorType: Flow<FingerprintSensorType?> =
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
@@ -66,16 +76,23 @@ class FingerprintEnrollViewModel(
* This flow should be the only flow which calls enroll().
*/
val _enrollFlow: Flow<FingerEnrollState> =
combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason ->
Pair(hardwareAuthToken, enrollReason)
combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason, enrollOptions) {
hardwareAuthToken,
enrollReason,
enrollOptions ->
Triple(hardwareAuthToken, enrollReason, enrollOptions)
}
.transformLatest {
/** [transformLatest] is used as we want to make sure to cancel previous API call. */
(hardwareAuthToken, enrollReason) ->
if (hardwareAuthToken is GatekeeperInfo.GatekeeperPasswordInfo && enrollReason != null) {
fingerprintManagerInteractor.enroll(hardwareAuthToken.token, enrollReason).collect {
emit(it)
}
(hardwareAuthToken, enrollReason, enrollOptions) ->
if (
hardwareAuthToken is GatekeeperInfo.GatekeeperPasswordInfo &&
enrollReason != null &&
enrollOptions != null
) {
fingerprintManagerInteractor
.enroll(hardwareAuthToken.token, enrollReason, enrollOptions)
.collect { emit(it) }
}
}
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
@@ -108,14 +125,23 @@ class FingerprintEnrollViewModel(
}
}
class FingerprintEnrollViewModelFactory(
val interactor: FingerprintManagerInteractor,
val gatekeeperViewModel: FingerprintGatekeeperViewModel,
val navigationViewModel: FingerprintNavigationViewModel,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) as T
/** Starts enrollment. */
fun enroll(options: FingerprintEnrollOptions) {
enrollOptions.update { options }
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication = this[APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!)
FingerprintEnrollViewModel(
biometricEnvironment!!.fingerprintManagerInteractor,
provider[FingerprintGatekeeperViewModel::class],
provider[FingerprintNavigationViewModel::class],
)
}
}
}
}

View File

@@ -19,21 +19,28 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.update
class FingerprintFlowViewModel(private val fingerprintFlowType: FingerprintFlow) : ViewModel() {
val fingerprintFlow: Flow<FingerprintFlow> =
flowOf(fingerprintFlowType).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
class FingerprintFlowViewModel() : ViewModel() {
val _mutableFingerprintFlow: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null)
val fingerprintFlow: Flow<FingerprintFlow?> =
_mutableFingerprintFlow.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
class FingerprintFlowViewModelFactory(val flowType: FingerprintFlow) : ViewModelProvider.Factory {
/** Used to set the fingerprint flow type */
fun updateFlowType(fingerprintFlowType: FingerprintFlow) {
_mutableFingerprintFlow.update { fingerprintFlowType }
}
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintFlowViewModel(flowType) as T
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { FingerprintFlowViewModel() }
}
}
}

View File

@@ -21,6 +21,9 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,12 +50,10 @@ sealed interface GatekeeperInfo {
* in as a parameter to this class.
*/
class FingerprintGatekeeperViewModel(
theGatekeeperInfo: GatekeeperInfo?,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
private val fingerprintManagerInteractor: FingerprintManagerInteractor
) : ViewModel() {
private var _gatekeeperInfo: MutableStateFlow<GatekeeperInfo?> =
MutableStateFlow(theGatekeeperInfo)
private var _gatekeeperInfo: MutableStateFlow<GatekeeperInfo?> = MutableStateFlow(null)
/** The gatekeeper info for fingerprint enrollment. */
val gatekeeperInfo: Flow<GatekeeperInfo?> = _gatekeeperInfo.asStateFlow()
@@ -61,26 +62,27 @@ class FingerprintGatekeeperViewModel(
val hasValidGatekeeperInfo: Flow<Boolean> =
gatekeeperInfo.map { it is GatekeeperInfo.GatekeeperPasswordInfo }
private var _credentialConfirmed: MutableStateFlow<Boolean?> = MutableStateFlow(null)
val credentialConfirmed: Flow<Boolean?> = _credentialConfirmed.asStateFlow()
private var countDownTimer: CountDownTimer? = null
/** Timeout of 15 minutes for a generated challenge */
private val TIMEOUT: Long = 15 * 60 * 1000
/** Called after a confirm device credential attempt has been made. */
fun onConfirmDevice(wasSuccessful: Boolean, theGatekeeperPasswordHandle: Long?) {
fun onConfirmDevice(
wasSuccessful: Boolean,
theGatekeeperPasswordHandle: Long?,
shouldStartTimer: Boolean = true,
) {
if (!wasSuccessful) {
Log.d(TAG, "confirmDevice failed")
_gatekeeperInfo.update { GatekeeperInfo.Invalid }
_credentialConfirmed.update { false }
} else {
viewModelScope.launch {
val res = fingerprintManagerInteractor.generateChallenge(theGatekeeperPasswordHandle!!)
_gatekeeperInfo.update { GatekeeperInfo.GatekeeperPasswordInfo(res.second, res.first) }
_credentialConfirmed.update { true }
startTimeout()
if (shouldStartTimer) {
startTimeout()
}
}
}
}
@@ -97,19 +99,9 @@ class FingerprintGatekeeperViewModel(
}
}
class FingerprintGatekeeperViewModelFactory(
private val gatekeeperInfo: GatekeeperInfo?,
private val fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintGatekeeperViewModel(gatekeeperInfo, fingerprintManagerInteractor) as T
}
}
companion object {
private const val TAG = "FingerprintGatekeeperViewModel"
/**
* A function that checks if the challenge and token are valid, in which case a
* [GatekeeperInfo.GatekeeperPasswordInfo] is provided, else [GatekeeperInfo.Invalid]
@@ -121,5 +113,14 @@ class FingerprintGatekeeperViewModel(
}
return GatekeeperInfo.GatekeeperPasswordInfo(token, challenge)
}
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
FingerprintGatekeeperViewModel(biometricEnvironment!!.fingerprintManagerInteractor)
}
}
}
}

View File

@@ -20,7 +20,11 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.android.settings.SettingsApplication
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.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.UiStep
@@ -28,39 +32,40 @@ import java.lang.NullPointerException
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
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() {
class FingerprintNavigationViewModel(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))
}
private val _flowInternal: MutableStateFlow<FingerprintFlow?> = MutableStateFlow(null)
private val _hasConfirmedDeviceCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _navStateInternal: StateFlow<NavigationState?> =
combine(
_flowInternal,
_hasConfirmedDeviceCredential,
fingerprintManagerInteractor.sensorPropertiesInternal,
) { flow, hasConfirmed, sensorType ->
if (flow == null || sensorType == null) {
return@combine null
}
.collect { navState -> _navStateInternal.update { navState } }
}
}
return@combine NavigationState(flow, hasConfirmed, sensorType)
}
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
private var _currentStep = MutableStateFlow<FingerprintNavigationStep?>(step)
private var _currentStep =
MutableStateFlow<FingerprintNavigationStep?>(FingerprintNavigationStep.Init)
private var _navigateTo: MutableStateFlow<UiStep?> = MutableStateFlow(null)
val navigateTo: Flow<UiStep?> = _navigateTo.asStateFlow()
@@ -85,6 +90,16 @@ class FingerprintNavigationViewModel(
/** This indicates what screen should currently be presenting to the user. */
val currentScreen: Flow<UiStep?> = _currentScreen.asStateFlow()
/** Updates the type of flow the navigation should begin */
fun updateFingerprintFlow(flow: FingerprintFlow) {
_flowInternal.update { flow }
}
/** Indicates if we have confirmed device credential */
fun hasConfirmedDeviceCredential(hasConfirmedDeviceCredential: Boolean) {
_hasConfirmedDeviceCredential.update { hasConfirmedDeviceCredential }
}
/** See [updateInternal] for more details */
fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) {
Log.d(TAG, "$caller.update($action) $debugStr")
@@ -122,26 +137,15 @@ class FingerprintNavigationViewModel(
}
}
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"
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val settingsApplication =
this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication
val biometricEnvironment = settingsApplication.biometricEnvironment
FingerprintNavigationViewModel(biometricEnvironment!!.fingerprintManagerInteractor)
}
}
}
}

View File

@@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -36,11 +38,9 @@ class FingerprintScrollViewModel : ViewModel() {
_hasReadConsentScreen.update { true }
}
class FingerprintScrollViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FingerprintScrollViewModel() as T
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { FingerprintScrollViewModel() }
}
}
}

View File

@@ -18,24 +18,15 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
/** Indicates the type of transitions that can occur between fragments */
sealed class Transition {
/**
* Indicates the new fragment should slide in from the left side
*/
/** Indicates the new fragment should slide in from the left side */
data object EnterFromLeft : Transition()
/**
* Indicates the new fragment should slide in from the right side
*/
/** Indicates the new fragment should slide in from the right side */
data object EnterFromRight : Transition()
/**
* Indicates the old fragment should slide out to the left side
*/
/** Indicates the old fragment should slide out to the left side */
data object ExitToLeft : Transition()
/**
* Indicates the old fragment should slide out to the right side
*/
/** Indicates the old fragment should slide out to the right side */
data object ExitToRight : Transition()
}

View File

@@ -54,29 +54,40 @@ object FingerprintSettingsViewBinder {
challenge: Long?,
challengeToken: ByteArray?,
)
/** Helper to launch an add fingerprint request */
fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?)
/**
* Helper function that will try and launch confirm lock, if that fails we will prompt user to
* choose a PIN/PATTERN/PASS.
*/
fun launchConfirmOrChooseLock(userId: Int)
/** Used to indicate that FingerprintSettings is finished. */
fun finish()
/** Indicates what result should be set for the returning callee */
fun setResultExternal(resultCode: Int)
/** Indicates the settings UI should be shown */
fun showSettings(enrolledFingerprints: List<FingerprintData>)
/** Updates the add fingerprints preference */
fun updateAddFingerprintsPreference(canEnroll: Boolean, maxFingerprints: Int)
/** Updates the sfps fingerprints preference */
fun updateSfpsPreference(isSfpsPrefVisible: Boolean)
/** Indicates that a user has been locked out */
fun userLockout(authAttemptViewModel: FingerprintAuthAttemptModel.Error)
/** Indicates a fingerprint preference should be highlighted */
suspend fun highlightPref(fingerId: Int)
/** Indicates a user should be prompted to delete a fingerprint */
suspend fun askUserToDeleteDialog(fingerprintViewModel: FingerprintData): Boolean
/** Indicates a user should be asked to renae ma dialog */
suspend fun askUserToRenameDialog(
fingerprintViewModel: FingerprintData

View File

@@ -45,14 +45,13 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
import com.android.settings.biometrics.fingerprint2.lib.model.Settings
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions
import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel
@@ -227,7 +226,6 @@ class FingerprintSettingsV2Fragment :
val fingerprintEnrollStateRepository =
FingerprintEnrollInteractorImpl(
requireContext().applicationContext,
intent.toFingerprintEnrollOptions(),
fingerprintManager,
Settings,
)

View File

@@ -52,7 +52,7 @@ class FingerprintSettingsNavigationViewModel(
_nextStep.update { LaunchConfirmDeviceCredential(userId) }
} else {
viewModelScope.launch {
if (fingerprintManagerInteractor.enrolledFingerprints.last().isEmpty()) {
if (fingerprintManagerInteractor.enrolledFingerprints.last()?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, null, challenge, token) }
} else {
showSettingsHelper()
@@ -149,7 +149,7 @@ class FingerprintSettingsNavigationViewModel(
private suspend fun launchEnrollNextStep(gateKeeperPasswordHandle: Long?) {
fingerprintManagerInteractor.enrolledFingerprints.collect {
if (it.isEmpty()) {
if (it?.isEmpty() == true) {
_nextStep.update { EnrollFirstFingerprint(userId, gateKeeperPasswordHandle, null, null) }
} else {
viewModelScope.launch(backgroundDispatcher) {

View File

@@ -74,7 +74,7 @@ class FingerprintSettingsViewModel(
/** Represents the stream of visibility of sfps preference. */
val isSfpsPrefVisible: Flow<Boolean> =
_enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform {
emit(fingerprintManagerInteractor.hasSideFps() && !it.isNullOrEmpty())
emit(fingerprintManagerInteractor.hasSideFps() == true && !it.isNullOrEmpty())
}
private val _isShowingDialog: MutableStateFlow<PreferenceViewModel?> = MutableStateFlow(null)

View File

@@ -67,7 +67,6 @@ class FingerprintEnrollIntroFragmentTest {
private val gatekeeperViewModel =
FingerprintGatekeeperViewModel(
GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
interactor
)
private val backgroundDispatcher = StandardTestDispatcher()
@@ -86,13 +85,10 @@ class FingerprintEnrollIntroFragmentTest {
.toFingerprintSensor()
var enrollFlow = Default
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
val flowViewModel = FingerprintFlowViewModel()
private val navigationViewModel =
FingerprintNavigationViewModel(
Introduction(),
false,
flowViewModel,
interactor
)
@@ -124,6 +120,11 @@ class FingerprintEnrollIntroFragmentTest {
}
}
gatekeeperViewModel.onConfirmDevice(true, 100L, false)
flowViewModel.updateFlowType(enrollFlow)
navigationViewModel.hasConfirmedDeviceCredential(true)
navigationViewModel.updateFingerprintFlow(enrollFlow)
fragmentScenario =
launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) {
FingerprintEnrollIntroV2Fragment(factory)

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

@@ -42,7 +42,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import kotlinx.coroutines.flow.Flow
@@ -95,23 +94,27 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
override fun getRotationFromDefault(rotation: Int): Int = rotation
}
var gatekeeperViewModel =
FingerprintGatekeeperViewModel(
GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
interactor,
)
var gatekeeperViewModel = FingerprintGatekeeperViewModel(fingerprintManagerInteractor)
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
val flowViewModel = FingerprintFlowViewModel()
var navigationViewModel = FingerprintNavigationViewModel(step, true, flowViewModel, interactor)
var navigationViewModel = FingerprintNavigationViewModel(fingerprintManagerInteractor)
var fingerprintViewModel =
FingerprintEnrollIntroViewModel(navigationViewModel, flowViewModel, interactor)
FingerprintEnrollIntroViewModel(
navigationViewModel,
flowViewModel,
fingerprintManagerInteractor,
)
var fingerprintScrollViewModel = FingerprintScrollViewModel()
var backgroundViewModel = BackgroundViewModel()
var fingerprintEnrollViewModel =
FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel)
FingerprintEnrollViewModel(
fingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
)
var fingerprintEnrollEnrollingViewModel =
FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
@@ -122,11 +125,11 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
interactor,
fingerprintManagerInteractor,
)
val fingerprintEnrollConfirmationViewModel =
FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor)
FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintManagerInteractor)
var fingerprintFindSensorViewModel =
FingerprintEnrollFindSensorViewModel(
@@ -134,11 +137,11 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
flowViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
flowViewModel,
interactor,
fingerprintManagerInteractor,
)
val factory =
@@ -166,12 +169,16 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
init {
fingerprintEnrollViewModel.sensorTypeCached = fingerprintSensor.sensorType
gatekeeperViewModel.onConfirmDevice(true, 100L)
navigationViewModel.updateFingerprintFlow(enrollFlow)
navigationViewModel.hasConfirmedDeviceCredential(true)
flowViewModel.updateFlowType(enrollFlow)
}
companion object {
private val Phone = DisplaySpec("phone", width = 1080, height = 2340, densityDpi = 420)
private const val screenshotPath = "/settings_screenshots"
val interactor = FakeFingerprintManagerInteractor()
val fingerprintManagerInteractor = FakeFingerprintManagerInteractor()
fun BiometricFragmentScreenShotRule() =
FragmentScreenshotTestRule(

View File

@@ -17,7 +17,6 @@
package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
@@ -28,17 +27,15 @@ import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollConfirmationScreenshotTest {
class RfpsEnrollConfirmationScreenshotTest {
private val injector: Injector = Injector(FingerprintNavigationStep.Confirmation)
@Rule
@JvmField
var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
@Rule @JvmField var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
@Test
fun testConfirmation() {
rule.screenshotTest(
"fp_enroll_confirmation",
"rfps_enroll_confirmation",
Mode.MatchSize,
FingerprintEnrollConfirmationV2Fragment(injector.factory),
)

View File

@@ -27,14 +27,14 @@ import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollEnrollingScreenshotTest {
class RfpsEnrollEnrollingScreenshotTest {
private val injector: Injector =
Injector(FingerprintNavigationStep.Enrollment(Injector.interactor.sensorProp))
Injector(FingerprintNavigationStep.Enrollment(Injector.fingerprintManagerInteractor.sensorProp))
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
@Test
fun testEnrollEnrolling() {
rule.screenshotTest("fp_enroll_enrolling", Mode.MatchSize, RFPSEnrollFragment(injector.factory))
rule.screenshotTest("rfps_enroll_enrolling", Mode.MatchSize, RFPSEnrollFragment(injector.factory))
}
}

View File

@@ -31,10 +31,6 @@ class RfpsEnrollFindSensorScreenshotTest {
@Test
fun testEnrollFindSensor() {
rule.screenshotTest(
"fp_enroll_find_sensor",
Mode.MatchSize,
RfpsEnrollFindSensorFragment(),
)
rule.screenshotTest("rfps_enroll_find_sensor", Mode.MatchSize, RfpsEnrollFindSensorFragment())
}
}

View File

@@ -27,17 +27,15 @@ import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollIntroScreenshotTest {
class RfpsEnrollIntroScreenshotTest {
private val injector: Injector = Injector(FingerprintNavigationStep.Introduction())
@Rule
@JvmField
var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
@Rule @JvmField var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
@Test
fun testEnrollIntro() {
rule.screenshotTest(
"fp_enroll_intro",
"rfps_enroll_intro",
Mode.MatchSize,
FingerprintEnrollIntroV2Fragment(injector.factory),
)

View File

@@ -19,6 +19,7 @@ package com.android.settings.testutils2
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.biometrics.SensorProperties
import android.hardware.fingerprint.FingerprintEnrollOptions
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
@@ -78,6 +79,7 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
override suspend fun enroll(
hardwareAuthToken: ByteArray?,
enrollReason: EnrollReason,
fingerprintEnrollOptions: FingerprintEnrollOptions
): Flow<FingerEnrollState> = flowOf(*enrollStateViewModel.toTypedArray())
override suspend fun removeFingerprint(fp: FingerprintData): Boolean {

View File

@@ -109,12 +109,7 @@ class FingerprintManagerInteractorTest {
fingerprintManager,
fingerprintSensorRepository,
gateKeeperPasswordProvider,
FingerprintEnrollInteractorImpl(
context,
FingerprintEnrollOptions.Builder().build(),
fingerprintManager,
Default,
),
FingerprintEnrollInteractorImpl(context, fingerprintManager, Default),
)
}
@@ -135,7 +130,7 @@ class FingerprintManagerInteractorTest {
whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList)
val list = underTest.enrolledFingerprints.last()
assertThat(list.size).isEqualTo(fingerprintList.size)
assertThat(list!!.size).isEqualTo(fingerprintList.size)
val actual = list[0]
assertThat(actual.name).isEqualTo(expected.name)
assertThat(actual.fingerId).isEqualTo(expected.biometricId)
@@ -318,7 +313,11 @@ class FingerprintManagerInteractorTest {
testScope.runTest {
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } }
val job = launch {
underTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
runCurrent()
@@ -343,7 +342,11 @@ class FingerprintManagerInteractorTest {
testScope.runTest {
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } }
val job = launch {
underTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
runCurrent()
@@ -368,7 +371,11 @@ class FingerprintManagerInteractorTest {
testScope.runTest {
val token = byteArrayOf(5, 3, 2)
var result: FingerEnrollState? = null
val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } }
val job = launch {
underTest
.enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build())
.collect { result = it }
}
val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
runCurrent()

View File

@@ -35,7 +35,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
@@ -90,45 +89,20 @@ class FingerprintEnrollFindSensorViewModelV2Test {
Dispatchers.setMain(backgroundDispatcher)
fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
gatekeeperViewModel =
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
null,
fakeFingerprintManagerInteractor,
)
.create(FingerprintGatekeeperViewModel::class.java)
gatekeeperViewModel = FingerprintGatekeeperViewModel(fakeFingerprintManagerInteractor)
val sensor =
FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_POWER_BUTTON,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
val fingerprintFlowViewModel = FingerprintFlowViewModel()
fingerprintFlowViewModel.updateFlowType(Default)
navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor)
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
navigationViewModel =
FingerprintNavigationViewModel(
FingerprintNavigationStep.Education(sensor),
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
)
backgroundViewModel =
BackgroundViewModel.BackgroundViewModelFactory().create(BackgroundViewModel::class.java)
backgroundViewModel = BackgroundViewModel()
backgroundViewModel.inForeground()
enrollViewModel =
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
fakeFingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
)
.create(FingerprintEnrollViewModel::class.java)
FingerprintEnrollViewModel(
fakeFingerprintManagerInteractor,
gatekeeperViewModel,
navigationViewModel,
)
accessibilityInteractor =
object : AccessibilityInteractor {
override val isAccessibilityEnabled: Flow<Boolean> = flowOf(false)
@@ -145,23 +119,23 @@ class FingerprintEnrollFindSensorViewModelV2Test {
orientationInteractor =
object : OrientationInteractor {
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
override val rotationFromDefault: Flow<Int> = flowOf(Surface.ROTATION_0)
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
override val rotationFromDefault: Flow<Int> = flowOf(Surface.ROTATION_0)
override fun getRotationFromDefault(rotation: Int): Int = rotation
}
underTest =
FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
navigationViewModel,
enrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
)
.create(FingerprintEnrollFindSensorViewModel::class.java)
FingerprintEnrollFindSensorViewModel(
navigationViewModel,
enrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
fingerprintFlowViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fakeFingerprintManagerInteractor,
)
}
@After

View File

@@ -23,22 +23,23 @@ import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
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.android.systemui.biometrics.shared.model.toFingerprintSensor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -53,44 +54,70 @@ class FingerprintEnrollConfirmationViewModelTest {
@get:Rule val instantTaskRule = InstantTaskExecutorRule()
private var backgroundDispatcher = StandardTestDispatcher()
private var testScope = TestScope(backgroundDispatcher)
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
val fingerprintFlowViewModel = FingerprintFlowViewModel()
val fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
lateinit var navigationViewModel: FingerprintNavigationViewModel
lateinit var underTest: FingerprintEnrollConfirmationViewModel
@Before
fun setup() {
navigationViewModel =
FingerprintNavigationViewModel(
FingerprintNavigationStep.Confirmation,
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
)
Dispatchers.setMain(backgroundDispatcher)
fingerprintFlowViewModel.updateFlowType(Default)
navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor)
underTest =
FingerprintEnrollConfirmationViewModel(navigationViewModel, fakeFingerprintManagerInteractor)
navigationViewModel.updateFingerprintFlow(Default)
navigationViewModel.hasConfirmedDeviceCredential(true)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
private fun bringToConfirmation() {
navigationViewModel.update(
FingerprintAction.NEXT,
FingerprintNavigationStep.Introduction::class,
"Intro.Test.NEXT",
)
navigationViewModel.update(
FingerprintAction.NEXT,
FingerprintNavigationStep.Education::class,
"Edu.Test.NEXT",
)
navigationViewModel.update(
FingerprintAction.NEXT,
FingerprintNavigationStep.Enrollment::class,
"Enrollment.Test.NEXT",
)
}
@Test
fun testCanEnrollFingerprints() =
testScope.runTest {
fakeFingerprintManagerInteractor.sensorProp = FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_POWER_BUTTON,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
advanceUntilIdle()
bringToConfirmation()
fakeFingerprintManagerInteractor.sensorProp =
FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
listOf<ComponentInfoInternal>(),
FingerprintSensorProperties.TYPE_POWER_BUTTON,
false /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = mutableListOf()
fakeFingerprintManagerInteractor.enrollableFingerprints = 5
var canEnrollFingerprints: Boolean = false
val job = launch { underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it } }
val job = launch {
underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it }
}
advanceUntilIdle()
assertThat(canEnrollFingerprints).isTrue()
@@ -100,12 +127,14 @@ class FingerprintEnrollConfirmationViewModelTest {
@Test
fun testNextButtonSendsNextStep() =
testScope.runTest {
advanceUntilIdle()
bringToConfirmation()
var step: FingerprintNavigationStep.UiStep? = null
val job = launch { navigationViewModel.navigateTo.collect { step = it } }
underTest.onNextButtonClicked()
runCurrent()
advanceUntilIdle()
assertThat(step).isNull()
job.cancel()
@@ -114,14 +143,18 @@ class FingerprintEnrollConfirmationViewModelTest {
@Test
fun testAddAnotherSendsAction() =
testScope.runTest {
advanceUntilIdle()
bringToConfirmation()
advanceUntilIdle()
var step: FingerprintNavigationStep.UiStep? = null
val job = launch { navigationViewModel.navigateTo.collect { step = it } }
underTest.onAddAnotherButtonClicked()
runCurrent()
advanceUntilIdle()
assertThat(step).isInstanceOf(FingerprintNavigationStep.Enrollment::class.java)
assertThat(step).isNull()
job.cancel()
}
}

View File

@@ -28,9 +28,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.toFingerprintSensor
import com.google.common.truth.Truth.assertThat
@@ -61,20 +59,15 @@ class FingerprintEnrollEnrollingViewModelTest {
private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private val defaultGatekeeperInfo = GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 3), 3)
private var testScope = TestScope(backgroundDispatcher)
private lateinit var fakeFingerprintManagerInteractor: FakeFingerprintManagerInteractor
private fun initialize(gatekeeperInfo: GatekeeperInfo = defaultGatekeeperInfo) {
private fun initialize() {
fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
gateKeeperViewModel =
FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
gatekeeperInfo,
fakeFingerprintManagerInteractor,
)
.create(FingerprintGatekeeperViewModel::class.java)
val sensor =
gateKeeperViewModel = FingerprintGatekeeperViewModel(fakeFingerprintManagerInteractor)
fakeFingerprintManagerInteractor.sensorProp =
FingerprintSensorPropertiesInternal(
1 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
@@ -86,32 +79,21 @@ class FingerprintEnrollEnrollingViewModelTest {
listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
)
.toFingerprintSensor()
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
val fingerprintFlowViewModel = FingerprintFlowViewModel()
fingerprintFlowViewModel.updateFlowType(Default)
navigationViewModel =
FingerprintNavigationViewModel(
Enrollment(sensor),
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
)
navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor)
backgroundViewModel =
BackgroundViewModel.BackgroundViewModelFactory().create(BackgroundViewModel::class.java)
backgroundViewModel = BackgroundViewModel()
backgroundViewModel.inForeground()
val fingerprintEnrollViewModel =
FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
fakeFingerprintManagerInteractor,
gateKeeperViewModel,
navigationViewModel,
)
.create(FingerprintEnrollViewModel::class.java)
FingerprintEnrollViewModel(
fakeFingerprintManagerInteractor,
gateKeeperViewModel,
navigationViewModel,
)
enrollEnrollingViewModel =
FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory(
fingerprintEnrollViewModel,
backgroundViewModel,
)
.create(FingerprintEnrollEnrollingViewModel::class.java)
FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
}
@Before
@@ -128,6 +110,7 @@ class FingerprintEnrollEnrollingViewModelTest {
@Test
fun testEnrollShouldBeFalse() =
testScope.runTest {
gateKeeperViewModel.onConfirmDevice(true, 3L, false)
var shouldEnroll = false
val job = launch {
@@ -147,6 +130,7 @@ class FingerprintEnrollEnrollingViewModelTest {
@Test
fun testEnrollShouldBeFalseWhenBackground() =
testScope.runTest {
gateKeeperViewModel.onConfirmDevice(true, 3L, false)
var shouldEnroll = false
val job = launch {