diff --git a/res/layout/fingerprint_enroll_find_sensor_graphic.xml b/res/layout/fingerprint_enroll_find_sensor_graphic.xml index 069045783e3..d818d48d36f 100644 --- a/res/layout/fingerprint_enroll_find_sensor_graphic.xml +++ b/res/layout/fingerprint_enroll_find_sensor_graphic.xml @@ -25,12 +25,12 @@ 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" /> + android:layout_height="match_parent" /> - + \ No newline at end of file diff --git a/res/layout/fingerprint_v2_enroll_find_sensor.xml b/res/layout/fingerprint_v2_enroll_find_sensor.xml index d2a495d0532..bff0cfb32e1 100644 --- a/res/layout/fingerprint_v2_enroll_find_sensor.xml +++ b/res/layout/fingerprint_v2_enroll_find_sensor.xml @@ -16,29 +16,30 @@ --> - - + android:layout_height="match_parent"> - - - + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="vertical"> - + + + + + \ No newline at end of file diff --git a/res/layout/fingerprint_v2_rfps_enroll_enrolling.xml b/res/layout/fingerprint_v2_rfps_enroll_enrolling.xml index 0b087d20b73..710f3fbac48 100644 --- a/res/layout/fingerprint_v2_rfps_enroll_enrolling.xml +++ b/res/layout/fingerprint_v2_rfps_enroll_enrolling.xml @@ -18,35 +18,35 @@ - - - + + + android:progress="0" + /> - + - + + + - - \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint2/OWNERS b/src/com/android/settings/biometrics/fingerprint2/OWNERS index c58a06d5fa0..f5fd453ff26 100644 --- a/src/com/android/settings/biometrics/fingerprint2/OWNERS +++ b/src/com/android/settings/biometrics/fingerprint2/OWNERS @@ -1,3 +1,4 @@ # Owners for Biometric Fingerprint joshmccloskey@google.com -jbolinger@google.com \ No newline at end of file +jbolinger@google.com +spdonghao@google.com \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt similarity index 96% rename from src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepo.kt rename to src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt index c045b0e7b9a..000a4774a78 100644 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepo.kt +++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt @@ -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 } -class FingerprintSensorRepoImpl( +class FingerprintSensorRepositoryImpl( fingerprintManager: FingerprintManager, backgroundDispatcher: CoroutineDispatcher, activityScope: CoroutineScope, -) : FingerprintSensorRepo { +) : FingerprintSensorRepository { override val fingerprintSensor: Flow = callbackFlow { diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/PressToAuthRepo.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/PressToAuthRepo.kt deleted file mode 100644 index 5909825b39f..00000000000 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/PressToAuthRepo.kt +++ /dev/null @@ -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) - } -} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt similarity index 57% rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt rename to src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt index 608b3706dbb..e7692372a31 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/AccessibilityViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt @@ -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,28 +25,28 @@ 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 +} + +class AccessibilityInteractorImpl( + accessibilityManager: AccessibilityManager, + activityScope: LifecycleCoroutineScope +) : AccessibilityInteractor { /** A flow that contains whether or not accessibility is enabled */ - val isAccessibilityEnabled: Flow = + override val isAccessibilityEnabled: Flow = 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( - viewModelScope, // This is going to tied to the view model scope - SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener - false, - ) - - class AccessibilityViewModelFactory(private val accessibilityManager: AccessibilityManager) : - ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return AccessibilityViewModel(accessibilityManager) as T } - } -} + .stateIn( + activityScope, // This is going to tied to the activity scope + SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener + false + ) +} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt index 1fbeb44249b..135a36ce538 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt @@ -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 -> val authenticationCallback = diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt new file mode 100644 index 00000000000..0224aa2ef93 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt @@ -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 + + /** + * 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 = 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) + } + +} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt similarity index 56% rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt rename to src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt index dd266e18285..968203fff80 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/OrientationStateViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt @@ -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 + /** A flow that contains the rotation info */ + val rotation: Flow + /** + * 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 = callbackFlow { +class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) : + OrientationInteractor { + + override val orientation: Flow = callbackFlow { val orientationEventListener = object : OrientationEventListener(context) { override fun onOrientationChanged(orientation: Int) { @@ -43,25 +55,24 @@ class OrientationStateViewModel(private val context: Context) : ViewModel() { awaitClose { orientationEventListener.disable() } } - /** A flow that contains the rotation info */ - val rotation: Flow = + override val rotation: Flow = callbackFlow { - val orientationEventListener = - object : OrientationEventListener(context) { - override fun onOrientationChanged(orientation: Int) { - trySend(getRotationFromDefault(context.display!!.rotation)) - } + val orientationEventListener = + object : OrientationEventListener(context) { + override fun onOrientationChanged(orientation: Int) { + trySend(getRotationFromDefault(context.display!!.rotation)) } - orientationEventListener.enable() - awaitClose { orientationEventListener.disable() } - } + } + orientationEventListener.enable() + 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 create(modelClass: Class): T { - return OrientationStateViewModel(context) as T - } - } -} +} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt new file mode 100644 index 00000000000..ab7b5de3f51 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt @@ -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 +} + +/** 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 = + + 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" + } +} diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt index 6e6df23da4e..c0e1b4aeaf4 100644 --- a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt @@ -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 } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt index f7e61356c88..9f3935bf070 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt @@ -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) { diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt index 9603e6b66ab..2b1ff9bf085 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt @@ -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,46 +92,43 @@ 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 + val view = inflater.inflate(contentLayoutId, container, false)!! as GlifLayout + setTexts(sensorType, view) - // Set up header and description - lifecycleScope.launch { viewModel.sensorType.collect { setTexts(it, view) } } + // Set up footer bar + val footerBarMixin = view.getMixin(FooterBarMixin::class.java) + setupSecondaryButton(footerBarMixin) + lifecycleScope.launch { + viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) } + } - // Set up footer bar - val footerBarMixin = view.getMixin(FooterBarMixin::class.java) - setupSecondaryButton(footerBarMixin) - lifecycleScope.launch { - viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) } - } - - // Set up lottie or animation - lifecycleScope.launch { - viewModel.sfpsLottieInfo.collect { (isFolded, rotation) -> - setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation)) - } - } - lifecycleScope.launch { - viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled -> - val lottieAnimation = - if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie - setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() } - } - } - lifecycleScope.launch { - viewModel.showRfpsAnimation.collect { - animation = view.findViewById(R.id.fingerprint_sensor_location_animation) - animation!!.startAnimation() - } - } - - lifecycleScope.launch { - viewModel.showErrorDialog.collect { (errMsgId, isSetup) -> - // TODO: Covert error dialog kotlin as well - FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup) - } + // Set up lottie or animation + lifecycleScope.launch { + viewModel.sfpsLottieInfo.collect { (isFolded, rotation) -> + setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation)) } } + lifecycleScope.launch { + viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled -> + val lottieAnimation = + if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie + setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() } + } + } + lifecycleScope.launch { + viewModel.showRfpsAnimation.collect { + animation = view.findViewById(R.id.fingerprint_sensor_location_animation) + animation!!.startAnimation() + } + } + + lifecycleScope.launch { + viewModel.showErrorDialog.collect { (errMsgId, isSetup) -> + // TODO: Covert error dialog kotlin as well + 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 -> { diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt index c6e284a4f00..a9cd16fef1e 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt @@ -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) } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt index 99250e6de11..2408a8873a8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSViewModel.kt @@ -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(false) + /** Value to indicate if the text view is visible or not */ val textViewIsVisible: Flow = _textViewIsVisible.asStateFlow() /** Indicates if the icon should be animating or not */ @@ -78,8 +80,12 @@ class RFPSViewModel( .filterIsInstance() .shareIn(viewModelScope, SharingStarted.Eagerly, 0) + /** Indicates that enrollment was completed. */ val didCompleteEnrollment: Flow = 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 create(modelClass: Class): T { - return RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel) as T + return RFPSViewModel( + fingerprintEnrollEnrollingViewModel, + navigationViewModel, + orientationInteractor, + ) + as T } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt index fe6268107d3..5a6fc149b3e 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt @@ -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() diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt index 92261b110e0..3bf806b7eaf 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt @@ -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 = - fingerprintEnrollViewModel.sensorType.shareIn( - viewModelScope, - SharingStarted.WhileSubscribed(), - 1, - ) + fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } private val _isUdfps: Flow = sensorType.map { it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC @@ -70,8 +69,8 @@ class FingerprintEnrollFindSensorViewModel( val sfpsLottieInfo: Flow> = 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 = - _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 create(modelClass: Class): T { @@ -200,10 +200,11 @@ class FingerprintEnrollFindSensorViewModel( fingerprintEnrollViewModel, gatekeeperViewModel, backgroundViewModel, - accessibilityViewModel, - foldStateViewModel, - orientationStateViewModel, + accessibilityInteractor, + foldStateInteractor, + orientationInteractor, fingerprintFlowViewModel, + fingerprintManagerInteractor, ) as T } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt index c2cff5e5334..c27808db434 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt @@ -56,7 +56,7 @@ class FingerprintEnrollViewModel( } /** Represents the stream of [FingerprintSensorType] */ - val sensorType: Flow = + val sensorType: Flow = fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } /** diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt deleted file mode 100644 index 94a70b057f4..00000000000 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FoldStateViewModel.kt +++ /dev/null @@ -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 = 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 create(modelClass: Class): T { - return FoldStateViewModel(context) as T - } - } -} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt index 05bb3298907..a5f20212180 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt @@ -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, ) diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/OWNERS b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/OWNERS new file mode 100644 index 00000000000..9f0a21aaaee --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/OWNERS @@ -0,0 +1 @@ +include /src/com/android/settings/biometrics/fingerprint2/OWNERS \ No newline at end of file diff --git a/tests/screenshot/AndroidManifest.xml b/tests/screenshot/AndroidManifest.xml index 6c8bb882104..9273270293c 100644 --- a/tests/screenshot/AndroidManifest.xml +++ b/tests/screenshot/AndroidManifest.xml @@ -23,6 +23,8 @@ + - \ No newline at end of file + diff --git a/tests/screenshot/assets/robolectric/fp_enroll_enrolling.png b/tests/screenshot/assets/robolectric/fp_enroll_enrolling.png new file mode 100644 index 00000000000..1299f2a39bf Binary files /dev/null and b/tests/screenshot/assets/robolectric/fp_enroll_enrolling.png differ diff --git a/tests/screenshot/assets/robolectric/fp_enroll_find_sensor.png b/tests/screenshot/assets/robolectric/fp_enroll_find_sensor.png new file mode 100644 index 00000000000..a252e5ecb51 Binary files /dev/null and b/tests/screenshot/assets/robolectric/fp_enroll_find_sensor.png differ diff --git a/tests/screenshot/assets/robolectric/fp_enroll_intro.png b/tests/screenshot/assets/robolectric/fp_enroll_intro.png index 308ab559292..3cd9f4a2dd1 100644 Binary files a/tests/screenshot/assets/robolectric/fp_enroll_intro.png and b/tests/screenshot/assets/robolectric/fp_enroll_intro.png differ diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt deleted file mode 100644 index 493a669a1af..00000000000 --- a/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt +++ /dev/null @@ -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 - 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 create(modelClass: Class): 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(R.id.enroll_intro_content_view)!! - view.setBackgroundColor(Color.BLACK) - } - fragmentScenario.onFragment { fragment -> - val view = fragment.requireView().findViewById(R.id.enroll_intro_content_view)!! - rule.assertBitmapAgainstGolden(viewToBitmap(view), "fp_enroll_intro", MSSIMMatcher()) - } - } -} diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/README.md b/tests/screenshot/src/com/android/settings/tests/screenshot/README.md new file mode 100644 index 00000000000..299265a9a08 --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/README.md @@ -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 diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt new file mode 100644 index 00000000000..84d76ff5501 --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt @@ -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 = flowOf(true) + } + + var foldStateInteractor = + object : FoldStateInteractor { + private val _foldState = MutableStateFlow(false) + override val isFolded: Flow = _foldState.asStateFlow() + + override fun onConfigurationChange(newConfig: Configuration) { + _foldState.update { false } + } + } + + var orientationInteractor = + object : OrientationInteractor { + override val orientation: Flow = flowOf(Configuration.ORIENTATION_LANDSCAPE) + override val rotation: Flow = 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 create(modelClass: Class): 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, + ) + } +} diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollEnrollingScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollEnrollingScreenshotTest.kt new file mode 100644 index 00000000000..215e76fc8c1 --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollEnrollingScreenshotTest.kt @@ -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)) + } +} diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollFindSensorScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollFindSensorScreenshotTest.kt new file mode 100644 index 00000000000..18257c2121b --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollFindSensorScreenshotTest.kt @@ -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), + ) + } +} diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt new file mode 100644 index 00000000000..40a23d7fd56 --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt @@ -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), + ) + } +} diff --git a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt index 7991ff142c6..f571dad420b 100644 --- a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt +++ b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt @@ -37,7 +37,6 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor { var authenticateAttempt = FingerprintAuthAttemptModel.Success(1) var enrollStateViewModel: List = 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 - } } diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt index f1808e38160..c284a6fee7f 100644 --- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt +++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt @@ -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 = flowOf(sensor) } @@ -96,9 +95,9 @@ class FingerprintManagerInteractorTest { context, backgroundDispatcher, fingerprintManager, - fingerprintSensorRepo, + fingerprintSensorRepository, gateKeeperPasswordProvider, - pressToAuthRepo, + pressToAuthInteractor, Default, ) } diff --git a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt index 3b02d49141b..f202c4fb7c3 100644 --- a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt +++ b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt @@ -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 = flowOf(false) + } + foldStateInteractor = + object : FoldStateInteractor { + override val isFolded: Flow = foldState + override fun onConfigurationChange(newConfig: Configuration) { + TODO("Not yet implemented") + } + } + orientationInteractor = + object: OrientationInteractor { + override val orientation: Flow + get() = TODO("Not yet implemented") + override val rotation: Flow = 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)