Adding more biometric screenshot tests

Test: atest FingerprintEnrollIntroScreenshotTest FingerprintEnrollFindSensorScreenshotTest FingerprintEnrollEnrollingScreenshotTest
Bug: 297083009
Change-Id: I11df6fbaefa9d333dcfe803577947a4be7af9882
This commit is contained in:
Joshua McCloskey
2024-02-13 18:56:07 +00:00
parent 98374376cc
commit d92faeb1cf
35 changed files with 805 additions and 554 deletions

View File

@@ -25,8 +25,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/security_settings_fingerprint_enroll_find_sensor_content_description"
android:src="@drawable/fingerprint_sensor_location"
android:scaleType="centerInside"/>
android:scaleType="centerInside"
android:src="@drawable/fingerprint_sensor_location" />
<com.android.settings.biometrics.fingerprint.FingerprintLocationAnimationView
android:id="@+id/fingerprint_sensor_location_animation"

View File

@@ -23,11 +23,12 @@ android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
android:clipChildren="false">
android:orientation="vertical">
<Space
android:layout_width="wrap_content"

View File

@@ -18,14 +18,15 @@
<com.google.android.setupdesign.GlifLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/fingerprint_layout_theme"
android:id="@+id/setup_wizard_layout"
style="?attr/fingerprint_layout_theme"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_view"
style="@style/SudContentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -46,7 +47,6 @@
android:layout_marginVertical="24dp"
android:paddingTop="0dp"
android:paddingBottom="0dp">
<com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fingerprint_progress_bar"
@@ -54,7 +54,8 @@
android:layout_height="match_parent"
android:background="@drawable/fp_illustration"
android:minHeight="@dimen/fingerprint_progress_bar_min_size"
android:progress="0" />
android:progress="0"
/>
</com.google.android.setupdesign.view.FillContentLayout>

View File

@@ -1,3 +1,4 @@
# Owners for Biometric Fingerprint
joshmccloskey@google.com
jbolinger@google.com
spdonghao@google.com

View File

@@ -39,16 +39,16 @@ import kotlinx.coroutines.withContext
*
* TODO(b/313493336): Move this to systemui
*/
interface FingerprintSensorRepo {
interface FingerprintSensorRepository {
/** Get the [FingerprintSensor] */
val fingerprintSensor: Flow<FingerprintSensor>
}
class FingerprintSensorRepoImpl(
class FingerprintSensorRepositoryImpl(
fingerprintManager: FingerprintManager,
backgroundDispatcher: CoroutineDispatcher,
activityScope: CoroutineScope,
) : FingerprintSensorRepo {
) : FingerprintSensorRepository {
override val fingerprintSensor: Flow<FingerprintSensor> =
callbackFlow {

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.data.repository
import android.content.Context
import android.provider.Settings
/** Interface that indicates if press to auth is on or off. */
interface PressToAuthRepo {
/** Indicates true if the PressToAuth feature is enabled, false otherwise. */
val isEnabled: Boolean
}
/** Indicates whether or not the press to auth feature is enabled. */
class PressToAuthRepoImpl(private val context: Context) : PressToAuthRepo {
/**
* Gets the status of the press to auth feature.
*
* Returns whether or not the press to auth feature is enabled.
*/
override val isEnabled: Boolean
get() {
var toReturn: Int =
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,
)
}
return (toReturn == 1)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.view.accessibility.AccessibilityManager
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -27,9 +25,17 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
/** Represents all of the information on accessibility state. */
class AccessibilityViewModel(accessibilityManager: AccessibilityManager) : ViewModel() {
interface AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
val isAccessibilityEnabled: Flow<Boolean> =
val isAccessibilityEnabled: Flow<Boolean>
}
class AccessibilityInteractorImpl(
accessibilityManager: AccessibilityManager,
activityScope: LifecycleCoroutineScope
) : AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
override val isAccessibilityEnabled: Flow<Boolean> =
callbackFlow {
val listener =
AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
@@ -39,16 +45,8 @@ class AccessibilityViewModel(accessibilityManager: AccessibilityManager) : ViewM
awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) }
}
.stateIn(
viewModelScope, // This is going to tied to the view model scope
activityScope, // This is going to tied to the activity scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
false,
false
)
class AccessibilityViewModelFactory(private val accessibilityManager: AccessibilityManager) :
ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AccessibilityViewModel(accessibilityManager) as T
}
}
}

View File

@@ -26,8 +26,7 @@ import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError
import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepo
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepo
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
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
@@ -57,9 +56,9 @@ class FingerprintManagerInteractorImpl(
applicationContext: Context,
private val backgroundDispatcher: CoroutineDispatcher,
private val fingerprintManager: FingerprintManager,
fingerprintSensorRepo: FingerprintSensorRepo,
fingerprintSensorRepository: FingerprintSensorRepository,
private val gatekeeperPasswordProvider: GatekeeperPasswordProvider,
private val pressToAuthRepo: PressToAuthRepo,
private val pressToAuthInteractor: PressToAuthInteractor,
private val fingerprintFlow: FingerprintFlow,
) : FingerprintManagerInteractor {
@@ -101,7 +100,7 @@ class FingerprintManagerInteractorImpl(
)
}
override val sensorPropertiesInternal = fingerprintSensorRepo.fingerprintSensor
override val sensorPropertiesInternal = fingerprintSensorRepository.fingerprintSensor
override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
@@ -211,10 +210,6 @@ class FingerprintManagerInteractorImpl(
it.resume(fingerprintManager.isPowerbuttonFps)
}
override suspend fun pressToAuthEnabled(): Boolean = suspendCancellableCoroutine {
it.resume(pressToAuthRepo.isEnabled)
}
override suspend fun authenticate(): FingerprintAuthAttemptModel =
suspendCancellableCoroutine { c: CancellableContinuation<FingerprintAuthAttemptModel> ->
val authenticationCallback =

View File

@@ -0,0 +1,58 @@
/*
* 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.content.Context
import android.content.res.Configuration
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
import com.android.systemui.unfold.updates.FoldProvider
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
interface FoldStateInteractor {
/** 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)
}
/**
* 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)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,25 +14,37 @@
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.view.OrientationEventListener
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.internal.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
/** Represents all of the information on orientation state and rotation state. */
class OrientationStateViewModel(private val context: Context) : ViewModel() {
/**
* Interactor which provides information about orientation
*/
interface OrientationInteractor {
/** A flow that contains the information about the orientation changing */
val orientation: Flow<Int>
/** A flow that contains the rotation info */
val rotation: Flow<Int>
/**
* A Helper function that computes rotation if device is in
* [R.bool.config_reverseDefaultConfigRotation]
*/
fun getRotationFromDefault(rotation: Int): Int
}
/** A flow that contains the orientation info */
val orientation: Flow<Int> = callbackFlow {
class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) :
OrientationInteractor {
override val orientation: Flow<Int> = callbackFlow {
val orientationEventListener =
object : OrientationEventListener(context) {
override fun onOrientationChanged(orientation: Int) {
@@ -43,8 +55,7 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
awaitClose { orientationEventListener.disable() }
}
/** A flow that contains the rotation info */
val rotation: Flow<Int> =
override val rotation: Flow<Int> =
callbackFlow {
val orientationEventListener =
object : OrientationEventListener(context) {
@@ -56,12 +67,12 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
awaitClose { orientationEventListener.disable() }
}
.stateIn(
viewModelScope, // This is going to tied to the view model scope
activityScope, // This is tied to the activity scope
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
context.display!!.rotation,
)
fun getRotationFromDefault(rotation: Int): Int {
override fun getRotationFromDefault(rotation: Int): Int {
val isReverseDefaultRotation =
context.resources.getBoolean(R.bool.config_reverseDefaultRotation)
return if (isReverseDefaultRotation) {
@@ -70,11 +81,4 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() {
rotation
}
}
class OrientationViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return OrientationStateViewModel(context) as T
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.content.Context
import android.database.ContentObserver
import android.provider.Settings
import android.util.Log
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
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 whether or not the press to auth feature is enabled. */
class PressToAuthInteractorImpl(
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)
}
}.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,
)
}
return toReturn == 1
}
companion object {
const val TAG = "PressToAuthInteractor"
}
}

View File

@@ -75,7 +75,4 @@ interface FingerprintManagerInteractor {
/** Indicates if the device has side fingerprint */
suspend fun hasSideFps(): Boolean
/** Indicates if the press to auth feature has been enabled */
suspend fun pressToAuthEnabled(): Boolean
}

View File

@@ -30,16 +30,20 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.internal.widget.LockPatternUtils
import com.android.settings.R
import com.android.settings.SettingsApplication
import com.android.settings.SetupWizardUtils
import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST
import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl
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.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
@@ -48,7 +52,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
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.viewmodel.AccessibilityViewModel
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.FingerprintEnrollEnrollingViewModel
@@ -66,9 +69,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
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.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.password.ChooseLockGeneric
import com.android.settings.password.ChooseLockSettingsHelper
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
@@ -90,9 +91,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
private lateinit var accessibilityViewModel: AccessibilityViewModel
private lateinit var foldStateViewModel: FoldStateViewModel
private lateinit var orientationStateViewModel: OrientationStateViewModel
private lateinit var foldStateInteractor: FoldStateInteractor
private lateinit var orientationInteractor: OrientationInteractor
private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
private lateinit var backgroundViewModel: BackgroundViewModel
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
@@ -127,7 +127,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
foldStateViewModel.onConfigurationChange(newConfig)
foldStateInteractor.onConfigurationChange(newConfig)
}
private fun onConfirmDevice(resultCode: Int, data: Intent?) {
@@ -179,7 +179,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
FingerprintFlowViewModel::class.java]
val fingerprintSensorRepo =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
val fingerprintManagerInteractor =
FingerprintManagerInteractorImpl(
@@ -188,7 +189,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
fingerprintManager,
fingerprintSensorRepo,
GatekeeperPasswordProvider(LockPatternUtils(context)),
PressToAuthRepoImpl(context),
pressToAuthInteractor,
enrollType,
)
@@ -198,6 +199,12 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
val hasConfirmedDeviceCredential = gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo
val accessibilityInteractor =
AccessibilityInteractorImpl(
getSystemService(AccessibilityManager::class.java)!!,
lifecycleScope,
)
navigationViewModel =
ViewModelProvider(
this,
@@ -228,10 +235,10 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
)[FingerprintGatekeeperViewModel::class.java]
// Initialize FoldStateViewModel
foldStateViewModel =
ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[
FoldStateViewModel::class.java]
foldStateViewModel.onConfigurationChange(resources.configuration)
foldStateInteractor = FoldStateInteractorImpl(context)
foldStateInteractor.onConfigurationChange(resources.configuration)
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
// Initialize FingerprintViewModel
fingerprintEnrollViewModel =
@@ -249,20 +256,6 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
FingerprintScrollViewModel::class.java]
// Initialize AccessibilityViewModel
accessibilityViewModel =
ViewModelProvider(
this,
AccessibilityViewModel.AccessibilityViewModelFactory(
getSystemService(AccessibilityManager::class.java)!!
),
)[AccessibilityViewModel::class.java]
// Initialize OrientationViewModel
orientationStateViewModel =
ViewModelProvider(this, OrientationStateViewModel.OrientationViewModelFactory(context))[
OrientationStateViewModel::class.java]
// Initialize FingerprintEnrollEnrollingViewModel
fingerprintEnrollEnrollingViewModel =
ViewModelProvider(
@@ -281,18 +274,24 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
),
)[FingerprintEnrollFindSensorViewModel::class.java]
// Initialize RFPS View Model
ViewModelProvider(
this,
RFPSViewModel.RFPSViewModelFactory(fingerprintEnrollEnrollingViewModel, navigationViewModel),
RFPSViewModel.RFPSViewModelFactory(
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
),
)[RFPSViewModel::class.java]
lifecycleScope.launch {
navigationViewModel.currentStep.collect { step ->
if (step is Init) {

View File

@@ -22,6 +22,7 @@ import android.view.LayoutInflater
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@@ -30,7 +31,6 @@ 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.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
@@ -51,12 +51,31 @@ private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
* will work.
*/
class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorType) : Fragment() {
/** Used for testing purposes */
private var factory: ViewModelProvider.Factory? = null
@VisibleForTesting
constructor(
sensorType: FingerprintSensorType,
theFactory: ViewModelProvider.Factory,
) : this(sensorType) {
factory = theFactory
}
private val viewModelProvider: ViewModelProvider by lazy {
if (factory != null) {
ViewModelProvider(requireActivity(), factory!!)
} else {
ViewModelProvider(requireActivity())
}
}
// This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
private var animation: FingerprintFindSensorAnimation? = null
private var contentLayoutId: Int = -1
private val viewModel: FingerprintEnrollFindSensorViewModel by lazy {
ViewModelProvider(requireActivity())[FingerprintEnrollFindSensorViewModel::class.java]
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
}
override fun onCreateView(
@@ -65,9 +84,6 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
savedInstanceState: Bundle?,
): View? {
val sensorType =
ViewModelProvider(requireActivity())[FingerprintEnrollViewModel::class.java].sensorTypeCached
contentLayoutId =
when (sensorType) {
FingerprintSensorType.UDFPS_OPTICAL,
@@ -76,11 +92,8 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
else -> R.layout.fingerprint_v2_enroll_find_sensor
}
return inflater.inflate(contentLayoutId, container, false).also { it ->
val view = it!! as GlifLayout
// Set up header and description
lifecycleScope.launch { viewModel.sensorType.collect { setTexts(it, view) } }
val view = inflater.inflate(contentLayoutId, container, false)!! as GlifLayout
setTexts(sensorType, view)
// Set up footer bar
val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
@@ -115,7 +128,7 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
}
}
}
return view
}
override fun onDestroy() {
@@ -158,7 +171,7 @@ class FingerprintEnrollFindSensorV2Fragment(val sensorType: FingerprintSensorTyp
illustrationLottie?.visibility = View.VISIBLE
}
private fun setTexts(sensorType: FingerprintSensorType, view: GlifLayout) {
private fun setTexts(sensorType: FingerprintSensorType?, view: GlifLayout) {
when (sensorType) {
FingerprintSensorType.UDFPS_OPTICAL,
FingerprintSensorType.UDFPS_ULTRASONIC -> {

View File

@@ -26,12 +26,14 @@ import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.view.animation.Interpolator
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
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.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
@@ -41,18 +43,34 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
/** This fragment is responsible for taking care of rear fingerprint enrollment. */
class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrolling) {
class RFPSEnrollFragment() : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrolling) {
/** 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 lateinit var linearOutSlowInInterpolator: Interpolator
private lateinit var fastOutLinearInInterpolator: Interpolator
@@ -60,24 +78,14 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
private lateinit var progressBar: RFPSProgressBar
private val iconTouchViewModel: RFPSIconTouchViewModel by lazy {
ViewModelProvider(requireActivity())[RFPSIconTouchViewModel::class.java]
viewModelProvider[RFPSIconTouchViewModel::class.java]
}
private val orientationViewModel: OrientationStateViewModel by lazy {
ViewModelProvider(requireActivity())[OrientationStateViewModel::class.java]
}
private val rfpsViewModel: RFPSViewModel by lazy {
ViewModelProvider(requireActivity())[RFPSViewModel::class.java]
}
private val rfpsViewModel: RFPSViewModel by lazy { viewModelProvider[RFPSViewModel::class.java] }
private val backgroundViewModel: BackgroundViewModel by lazy {
ViewModelProvider(requireActivity())[BackgroundViewModel::class.java]
viewModelProvider[BackgroundViewModel::class.java]
}
private val navigationViewModel: FingerprintNavigationViewModel by lazy {
ViewModelProvider(requireActivity())[FingerprintNavigationViewModel::class.java]
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -115,9 +123,8 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
true
}
// On any orientation event, dismiss dialogs.
viewLifecycleOwner.lifecycleScope.launch {
orientationViewModel.orientation.collect { dismissDialogs() }
rfpsViewModel.shouldDismissDialog.collect { dismissDialogs() }
}
// Signal we are ready for enrollment.
@@ -127,6 +134,8 @@ class RFPSEnrollFragment : Fragment(R.layout.fingerprint_v2_rfps_enroll_enrollin
repeatOnLifecycle(Lifecycle.State.RESUMED) {
// Icon animation update
viewLifecycleOwner.lifecycleScope.launch {
// TODO(b/324427704): Fix this delay
delay(100)
rfpsViewModel.shouldAnimateIcon.collect { animate ->
progressBar.updateIconAnimation(animate)
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
@@ -39,10 +40,11 @@ import kotlinx.coroutines.flow.update
class RFPSViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
orientationInteractor: OrientationInteractor,
) : ViewModel() {
/** Value to indicate if the text view is visible or not */
private val _textViewIsVisible = MutableStateFlow<Boolean>(false)
/** Value to indicate if the text view is visible or not */
val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()
/** Indicates if the icon should be animating or not */
@@ -78,8 +80,12 @@ class RFPSViewModel(
.filterIsInstance<FingerEnrollState.EnrollError>()
.shareIn(viewModelScope, SharingStarted.Eagerly, 0)
/** Indicates that enrollment was completed. */
val didCompleteEnrollment: Flow<Boolean> = progress.filterNotNull().map { it.remainingSteps == 0 }
/** Indicates if the fragment should dismiss a dialog if one was shown. */
val shouldDismissDialog = orientationInteractor.orientation.map { true }
/** Indicates if the consumer is ready for enrollment */
fun readyForEnrollment() {
fingerprintEnrollViewModel.canEnroll()
@@ -90,6 +96,7 @@ class RFPSViewModel(
fingerprintEnrollViewModel.stopEnroll()
}
/** Set the visibility of the text view */
fun setVisibility(isVisible: Boolean) {
_textViewIsVisible.update { isVisible }
}
@@ -122,6 +129,7 @@ class RFPSViewModel(
)
}
/** Indicates that enrollment has been finished and we can proceed to the next step. */
fun finishedSuccessfully() {
navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished")
}
@@ -129,11 +137,17 @@ class RFPSViewModel(
class RFPSViewModelFactory(
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
private val navigationViewModel: FingerprintNavigationViewModel,
private val orientationInteractor: OrientationInteractor,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel) as T
return RFPSViewModel(
fingerprintEnrollEnrollingViewModel,
navigationViewModel,
orientationInteractor,
)
as T
}
}

View File

@@ -24,14 +24,14 @@ import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.util.AttributeSet
import android.util.Log
import android.view.animation.AnimationUtils
import android.view.animation.Interpolator
import com.android.settings.R
import com.android.settings.widget.RingProgressBar
/** Progress bar for rear fingerprint enrollment. */
class RFPSProgressBar(context: Context, attributeSet: AttributeSet) :
RingProgressBar(context, attributeSet) {
class RFPSProgressBar : RingProgressBar {
private val fastOutSlowInInterpolator: Interpolator
@@ -42,9 +42,9 @@ class RFPSProgressBar(context: Context, attributeSet: AttributeSet) :
private var progressAnimation: ObjectAnimator? = null
private var shouldAnimateInternal: Boolean = true
private var shouldAnimateInternal: Boolean = false
init {
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
val fingerprintDrawable = background as LayerDrawable
iconAnimationDrawable =
fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_animation)
@@ -52,10 +52,8 @@ class RFPSProgressBar(context: Context, attributeSet: AttributeSet) :
iconBackgroundBlinksDrawable =
fingerprintDrawable.findDrawableByLayerId(R.id.fingerprint_background)
as AnimatedVectorDrawable
fastOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in)
iconAnimationDrawable.registerAnimationCallback(
object : Animatable2.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
@@ -66,7 +64,6 @@ class RFPSProgressBar(context: Context, attributeSet: AttributeSet) :
}
}
)
animateIconAnimationInternal()
progressBackgroundTintMode = PorterDuff.Mode.SRC
@@ -85,8 +82,8 @@ class RFPSProgressBar(context: Context, attributeSet: AttributeSet) :
}
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

@@ -19,6 +19,10 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
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
import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
@@ -26,13 +30,11 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
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.combineTransform
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -42,19 +44,16 @@ class FingerprintEnrollFindSensorViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
backgroundViewModel: BackgroundViewModel,
accessibilityViewModel: AccessibilityViewModel,
foldStateViewModel: FoldStateViewModel,
orientationStateViewModel: OrientationStateViewModel,
accessibilityInteractor: AccessibilityInteractor,
foldStateInteractor: FoldStateInteractor,
orientationInteractor: OrientationInteractor,
fingerprintFlowViewModel: FingerprintFlowViewModel,
fingerprintManagerInteractor: FingerprintManagerInteractor,
) : ViewModel() {
/** Represents the stream of sensor type. */
val sensorType: Flow<FingerprintSensorType> =
fingerprintEnrollViewModel.sensorType.shareIn(
viewModelScope,
SharingStarted.WhileSubscribed(),
1,
)
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
private val _isUdfps: Flow<Boolean> =
sensorType.map {
it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
@@ -70,8 +69,8 @@ class FingerprintEnrollFindSensorViewModel(
val sfpsLottieInfo: Flow<Pair<Boolean, Int>> =
combineTransform(
_showSfpsLottie,
foldStateViewModel.isFolded,
orientationStateViewModel.rotation,
foldStateInteractor.isFolded,
orientationInteractor.rotation,
) { _, isFolded, rotation ->
emit(Pair(isFolded, rotation))
}
@@ -79,7 +78,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(accessibilityViewModel.isAccessibilityEnabled) {
_showUdfpsLottie.combine(accessibilityInteractor.isAccessibilityEnabled) {
_,
isAccessibilityEnabled ->
isAccessibilityEnabled
@@ -104,7 +103,7 @@ class FingerprintEnrollFindSensorViewModel(
// Start or end enroll flow
viewModelScope.launch {
combine(
fingerprintEnrollViewModel.sensorType,
sensorType,
gatekeeperViewModel.hasValidGatekeeperInfo,
gatekeeperViewModel.gatekeeperInfo,
navigationViewModel.currentScreen,
@@ -188,10 +187,11 @@ class FingerprintEnrollFindSensorViewModel(
private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
private val backgroundViewModel: BackgroundViewModel,
private val accessibilityViewModel: AccessibilityViewModel,
private val foldStateViewModel: FoldStateViewModel,
private val orientationStateViewModel: OrientationStateViewModel,
private val 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 {
@@ -200,10 +200,11 @@ class FingerprintEnrollFindSensorViewModel(
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fingerprintManagerInteractor,
)
as T
}

View File

@@ -56,7 +56,7 @@ class FingerprintEnrollViewModel(
}
/** Represents the stream of [FingerprintSensorType] */
val sensorType: Flow<FingerprintSensorType> =
val sensorType: Flow<FingerprintSensorType?> =
fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType }
/**

View File

@@ -1,56 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
import android.content.Context
import android.content.res.Configuration
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
import com.android.systemui.unfold.updates.FoldProvider
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
/** Represents all of the information on fold state. */
class FoldStateViewModel(context: Context) : ViewModel() {
private val screenSizeFoldProvider = ScreenSizeFoldProvider(context)
/** A flow that contains the fold state info */
val isFolded: Flow<Boolean> = callbackFlow {
val foldStateListener =
object : FoldProvider.FoldCallback {
override fun onFoldUpdated(isFolded: Boolean) {
trySend(isFolded)
}
}
screenSizeFoldProvider.registerCallback(foldStateListener, context.mainExecutor)
awaitClose { screenSizeFoldProvider.unregisterCallback(foldStateListener) }
}
fun onConfigurationChange(newConfig: Configuration) {
screenSizeFoldProvider.onConfigurationChange(newConfig)
}
class FoldStateViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FoldStateViewModel(context) as T
}
}
}

View File

@@ -45,8 +45,8 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepoImpl
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData
@@ -220,7 +220,8 @@ class FingerprintSettingsV2Fragment :
toReturn == 1
}
val fingerprintSensorProvider =
FingerprintSensorRepoImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope)
val pressToAuthInteractor = PressToAuthInteractorImpl(context, backgroundDispatcher)
val interactor =
FingerprintManagerInteractorImpl(
@@ -229,7 +230,7 @@ class FingerprintSettingsV2Fragment :
fingerprintManager,
fingerprintSensorProvider,
GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)),
PressToAuthRepoImpl(context),
pressToAuthInteractor,
Settings,
)

View File

@@ -0,0 +1 @@
include /src/com/android/settings/biometrics/fingerprint2/OWNERS

View File

@@ -23,6 +23,8 @@
<application>
<activity android:name="com.android.settings.test.screenshot.ContainerActivity" android:exported="true" />
<activity android:name="platform.test.screenshot.FragmentScreenshotActivity" android:exported="true"
android:theme="@style/GlifTheme.Light" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

@@ -1,142 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.tests.screenshot
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.os.Bundle
import android.view.View
import androidx.fragment.app.testing.FragmentScenario
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import kotlinx.coroutines.test.StandardTestDispatcher
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import platform.test.screenshot.GoldenImagePathManager
import platform.test.screenshot.ScreenshotTestRule
import platform.test.screenshot.matchers.MSSIMMatcher
@RunWith(AndroidJUnit4::class)
class BasicScreenshotTest {
@Rule
@JvmField
var rule: ScreenshotTestRule =
ScreenshotTestRule(
GoldenImagePathManager(
InstrumentationRegistry.getInstrumentation().getContext(),
InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getFilesDir()
.getAbsolutePath() + "/settings_screenshots",
)
)
private var context: Context = ApplicationProvider.getApplicationContext()
private var interactor = FakeFingerprintManagerInteractor()
private val gatekeeperViewModel =
FingerprintGatekeeperViewModel(
GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
interactor,
)
private val backgroundDispatcher = StandardTestDispatcher()
private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
private val fingerprintSensor =
FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
var enrollFlow = Default
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
private val navigationViewModel =
FingerprintNavigationViewModel(
FingerprintNavigationStep.Introduction,
false,
flowViewModel,
interactor,
)
private var fingerprintViewModel =
FingerprintEnrollIntroViewModel(navigationViewModel, flowViewModel, interactor)
private var fingerprintScrollViewModel = FingerprintScrollViewModel()
@Before
fun setup() {
val factory =
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when (modelClass) {
FingerprintEnrollIntroViewModel::class.java -> fingerprintViewModel
FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
FingerprintNavigationViewModel::class.java -> navigationViewModel
FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
else -> null
}
as T
}
}
fragmentScenario =
launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) {
FingerprintEnrollIntroV2Fragment(factory)
}
}
/** Renders a [view] into a [Bitmap]. */
private fun viewToBitmap(view: View): Bitmap {
val bitmap =
Bitmap.createBitmap(view.measuredWidth, view.measuredHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
@Test
fun testEnrollIntro() {
fragmentScenario.onFragment { fragment ->
val view = fragment.requireView().findViewById<View>(R.id.enroll_intro_content_view)!!
view.setBackgroundColor(Color.BLACK)
}
fragmentScenario.onFragment { fragment ->
val view = fragment.requireView().findViewById<View>(R.id.enroll_intro_content_view)!!
rule.assertBitmapAgainstGolden(viewToBitmap(view), "fp_enroll_intro", MSSIMMatcher())
}
}
}

View File

@@ -0,0 +1,12 @@
# Background info about tests
1. This test is ran in postsubmits at andoid-settings/robo_tests.gcl
2. It is important that this module stays somewhat small, if the test size grows
too large, it will be likely that this suite breaks due to flakiness(which
tends to happen with screenshot tests). In this case investigate splitting
the module.
# Running and updating screenshots.
1. For FingerprintEnrollIntroScreenshotTest.kt#testEnrollIntro
2. atest SettingsScreenshotRNGTests
3. There should be a file like com.android.settings.tests.screenshot.biometrics.fingerprint.fragment.FingerprintEnrollIntroScreenshotTest_testEnrollIntro_actual_robolectric_fp_enroll_intro.png_6245562387930305138.png
4. Place this screenshot in packages/apps/Settings/tests/screenshot/assets/robolectric/fp_enroll_intro.png

View File

@@ -0,0 +1,161 @@
/*
* 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.tests.screenshot.biometrics.fingerprint
import android.content.res.Configuration
import android.view.Surface
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.test.platform.app.InstrumentationRegistry
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
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
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
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.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.update
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.DisplaySpec
import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.GoldenImagePathManager
import platform.test.screenshot.matchers.PixelPerfectMatcher
class Injector(step: FingerprintNavigationStep.UiStep) {
var enrollFlow = Default
var fingerprintSensor = FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.REAR)
var accessibilityInteractor =
object : AccessibilityInteractor {
override val isAccessibilityEnabled: Flow<Boolean> = flowOf(true)
}
var foldStateInteractor =
object : FoldStateInteractor {
private val _foldState = MutableStateFlow(false)
override val isFolded: Flow<Boolean> = _foldState.asStateFlow()
override fun onConfigurationChange(newConfig: Configuration) {
_foldState.update { false }
}
}
var orientationInteractor =
object : OrientationInteractor {
override val orientation: Flow<Int> = flowOf(Configuration.ORIENTATION_LANDSCAPE)
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
override fun getRotationFromDefault(rotation: Int): Int = rotation
}
var gatekeeperViewModel =
FingerprintGatekeeperViewModel(
GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
interactor,
)
val flowViewModel = FingerprintFlowViewModel(enrollFlow)
var navigationViewModel = FingerprintNavigationViewModel(step, true, flowViewModel, interactor)
var fingerprintViewModel =
FingerprintEnrollIntroViewModel(navigationViewModel, flowViewModel, interactor)
var fingerprintScrollViewModel = FingerprintScrollViewModel()
var backgroundViewModel = BackgroundViewModel()
var fingerprintEnrollViewModel =
FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel)
var fingerprintEnrollEnrollingViewModel =
FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel)
var rfpsIconTouchViewModel = RFPSIconTouchViewModel()
var rfpsViewModel =
RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor)
var fingerprintFindSensorViewModel =
FingerprintEnrollFindSensorViewModel(
navigationViewModel,
fingerprintEnrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
flowViewModel,
interactor,
)
val factory =
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when (modelClass) {
FingerprintEnrollIntroViewModel::class.java -> fingerprintViewModel
FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
FingerprintNavigationViewModel::class.java -> navigationViewModel
FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
FingerprintEnrollFindSensorViewModel::class.java -> fingerprintFindSensorViewModel
FingerprintEnrollViewModel::class.java -> fingerprintEnrollViewModel
RFPSViewModel::class.java -> rfpsViewModel
BackgroundViewModel::class.java -> backgroundViewModel
RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel
FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel
else -> null
}
as T
}
}
init {
fingerprintEnrollViewModel.sensorTypeCached = fingerprintSensor.sensorType
}
companion object {
private val Phone = DisplaySpec("phone", width = 1080, height = 2340, densityDpi = 420)
private const val screenshotPath = "/settings_screenshots"
val interactor = FakeFingerprintManagerInteractor()
fun BiometricFragmentScreenShotRule() =
FragmentScreenshotTestRule(
DeviceEmulationSpec.forDisplays(Phone).first(),
GoldenImagePathManager(
InstrumentationRegistry.getInstrumentation().context,
InstrumentationRegistry.getInstrumentation().targetContext.filesDir.absolutePath +
screenshotPath,
),
PixelPerfectMatcher(),
true,
)
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.tests.screenshot.biometrics.fingerprint.fragment
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollEnrollingScreenshotTest {
private val injector: Injector =
Injector(FingerprintNavigationStep.Enrollment(Injector.interactor.sensorProp))
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
@Test
fun testEnrollEnrolling() {
rule.screenshotTest("fp_enroll_enrolling", Mode.MatchSize, RFPSEnrollFragment(injector.factory))
}
}

View File

@@ -0,0 +1,45 @@
package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment
/*
* 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.
*/
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector.Companion.BiometricFragmentScreenShotRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollFindSensorScreenshotTest {
private val injector: Injector =
Injector(FingerprintNavigationStep.Education(Injector.interactor.sensorProp))
@Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule()
@Test
fun testEnrollFindSensor() {
rule.screenshotTest(
"fp_enroll_find_sensor",
Mode.MatchSize,
FingerprintEnrollFindSensorV2Fragment(injector.fingerprintSensor.sensorType, injector.factory),
)
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import platform.test.screenshot.FragmentScreenshotTestRule
import platform.test.screenshot.ViewScreenshotTestRule.Mode
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollIntroScreenshotTest {
private val injector: Injector = Injector(FingerprintNavigationStep.Introduction)
@Rule
@JvmField
var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule()
@Test
fun testEnrollIntro() {
rule.screenshotTest(
"fp_enroll_intro",
Mode.MatchSize,
FingerprintEnrollIntroV2Fragment(injector.factory),
)
}
}

View File

@@ -37,7 +37,6 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
var authenticateAttempt = FingerprintAuthAttemptModel.Success(1)
var enrollStateViewModel: List<FingerEnrollState> =
listOf(FingerEnrollState.EnrollProgress(5, 5))
var pressToAuthEnabled = true
var sensorProp =
FingerprintSensor(
@@ -86,7 +85,4 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
return sensorProp.sensorType == FingerprintSensorType.POWER_BUTTON
}
override suspend fun pressToAuthEnabled(): Boolean {
return pressToAuthEnabled
}
}

View File

@@ -26,9 +26,9 @@ import android.os.CancellationSignal
import android.os.Handler
import androidx.test.core.app.ApplicationProvider
import com.android.settings.biometrics.GatekeeperPasswordProvider
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepo
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
import com.android.settings.biometrics.fingerprint2.data.repository.PressToAuthRepo
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
@@ -77,17 +77,16 @@ class FingerprintManagerInteractorTest {
@Mock private lateinit var gateKeeperPasswordProvider: GatekeeperPasswordProvider
private var testScope = TestScope(backgroundDispatcher)
private var pressToAuthRepo =
object : PressToAuthRepo {
override val isEnabled: Boolean
get() = false
private var pressToAuthInteractor =
object : PressToAuthInteractor {
override val isEnabled = flowOf(false)
}
@Before
fun setup() {
val sensor = FingerprintSensor(1, SensorStrength.STRONG, 5, FingerprintSensorType.POWER_BUTTON)
val fingerprintSensorRepo =
object : FingerprintSensorRepo {
val fingerprintSensorRepository =
object : FingerprintSensorRepository {
override val fingerprintSensor: Flow<FingerprintSensor> = flowOf(sensor)
}
@@ -96,9 +95,9 @@ class FingerprintManagerInteractorTest {
context,
backgroundDispatcher,
fingerprintManager,
fingerprintSensorRepo,
fingerprintSensorRepository,
gateKeeperPasswordProvider,
pressToAuthRepo,
pressToAuthInteractor,
Default,
)
}

View File

@@ -18,27 +18,30 @@ package com.android.settings.fingerprint2.enrollment.viewmodel
import android.content.Context
import android.content.res.Configuration
import android.view.accessibility.AccessibilityManager
import android.view.Surface
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
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
import com.android.settings.biometrics.fingerprint2.lib.model.Default
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavigationState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.testutils2.FakeFingerprintManagerInteractor
import com.android.systemui.biometrics.shared.model.FingerprintSensor
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -68,14 +71,13 @@ class FingerprintEnrollFindSensorViewModelV2Test {
private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
private lateinit var enrollViewModel: FingerprintEnrollViewModel
private lateinit var navigationViewModel: FingerprintNavigationViewModel
private lateinit var accessibilityViewModel: AccessibilityViewModel
private lateinit var foldStateViewModel: FoldStateViewModel
private lateinit var orientationStateViewModel: OrientationStateViewModel
private lateinit var accessibilityInteractor: AccessibilityInteractor
private lateinit var foldStateInteractor: FoldStateInteractor
private lateinit var orientationInteractor: OrientationInteractor
private lateinit var underTest: FingerprintEnrollFindSensorViewModel
private lateinit var backgroundViewModel: BackgroundViewModel
private val context: Context = ApplicationProvider.getApplicationContext()
private val accessibilityManager: AccessibilityManager =
context.getSystemService(AccessibilityManager::class.java)!!
private val foldState = MutableStateFlow(false)
@Before
fun setup() {
@@ -95,7 +97,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
val fingerprintFlowViewModel = FingerprintFlowViewModel(Default)
navigationViewModel =
FingerprintNavigationViewModel(
Education(sensor),
FingerprintNavigationStep.Education(sensor),
false,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
@@ -111,24 +113,39 @@ class FingerprintEnrollFindSensorViewModelV2Test {
navigationViewModel,
)
.create(FingerprintEnrollViewModel::class.java)
accessibilityViewModel =
AccessibilityViewModel.AccessibilityViewModelFactory(accessibilityManager)
.create(AccessibilityViewModel::class.java)
foldStateViewModel =
FoldStateViewModel.FoldStateViewModelFactory(context).create(FoldStateViewModel::class.java)
orientationStateViewModel =
OrientationStateViewModel.OrientationViewModelFactory(context)
.create(OrientationStateViewModel::class.java)
accessibilityInteractor =
object : AccessibilityInteractor {
override val isAccessibilityEnabled: Flow<Boolean> = flowOf(false)
}
foldStateInteractor =
object : FoldStateInteractor {
override val isFolded: Flow<Boolean> = foldState
override fun onConfigurationChange(newConfig: Configuration) {
TODO("Not yet implemented")
}
}
orientationInteractor =
object: OrientationInteractor {
override val orientation: Flow<Int>
get() = TODO("Not yet implemented")
override val rotation: Flow<Int> = flowOf(Surface.ROTATION_0)
override fun getRotationFromDefault(rotation: Int): Int {
TODO("Not yet implemented")
}
}
underTest =
FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
navigationViewModel,
enrollViewModel,
gatekeeperViewModel,
backgroundViewModel,
accessibilityViewModel,
foldStateViewModel,
orientationStateViewModel,
accessibilityInteractor,
foldStateInteractor,
orientationInteractor,
fingerprintFlowViewModel,
fakeFingerprintManagerInteractor,
)
.create(FingerprintEnrollFindSensorViewModel::class.java)
}
@@ -171,8 +188,8 @@ class FingerprintEnrollFindSensorViewModelV2Test {
}
}
val config = createConfiguration(isFolded = true)
foldStateViewModel.onConfigurationChange(config)
foldState.update { true }
advanceUntilIdle()
assertThat(isFolded).isTrue()
assertThat(rotation).isEqualTo(context.display!!.rotation)
@@ -191,8 +208,8 @@ class FingerprintEnrollFindSensorViewModelV2Test {
}
}
val config = createConfiguration(isFolded = false)
foldStateViewModel.onConfigurationChange(config)
foldState.update { false }
advanceUntilIdle()
assertThat(isFolded).isFalse()
assertThat(rotation).isEqualTo(context.display!!.rotation)