From 584b6c9e965e0b18610a296929b2b1c2d0044468 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Wed, 1 May 2024 22:30:25 +0000 Subject: [PATCH] Biometrics Enrollment refactor (7/N) This cl moves the creation of repos and interactors to the SettingsApplication. Bug: 297082837 Test: atest Change-Id: I9049da6f03bb1dc18d4186961444bf613d773d0e --- Android.bp | 2 + .../android/settings/SettingsApplication.java | 9 + .../fingerprint/UdfpsEnrollHelper.java | 1 - .../fingerprint2/BiometricsEnvironment.kt | 144 ++++++++++ .../data/repository/DebuggingRepository.kt | 1 + .../repository/FingerprintSensorRepository.kt | 7 +- .../SimulatedTouchEventsRepository.kt | 7 +- .../repository/UdfpsEnrollDebugRepository.kt | 68 +++-- .../DebugTouchEventInteractorImpl.kt | 29 ++ .../interactor/AccessibilityInteractor.kt | 22 +- .../domain/interactor/DebuggingInteractor.kt | 4 +- .../interactor/DisplayDensityInteractor.kt | 24 +- .../interactor/FingerprintEnrollInteractor.kt | 8 +- .../FingerprintManagerInteractorImpl.kt | 39 +-- .../interactor/FingerprintSensorInteractor.kt | 34 +++ .../domain/interactor/FoldStateInteractor.kt | 46 ++- .../interactor/OrientationInteractor.kt | 5 +- .../interactor/PressToAuthInteractor.kt | 112 ++++---- .../domain/interactor/TouchEventInteractor.kt | 27 ++ .../interactor/UdfpsEnrollInteractor.kt | 13 +- .../domain/interactor/VibrationInteractor.kt | 6 +- .../FingerprintManagerInteractor.kt | 7 +- .../fingerprint2/lib/model/EnrollReason.kt | 2 +- .../FingerprintEnrollmentV2Activity.kt | 269 +++--------------- ...FingerprintEnrollConfirmationV2Fragment.kt | 32 +-- .../FingerprintEnrollIntroV2Fragment.kt | 34 +-- .../education/RfpsEnrollFindSensorFragment.kt | 22 +- .../education/SfpsEnrollFindSensorFragment.kt | 22 +- .../UdfpsEnrollFindSensorFragment.kt | 22 +- .../rfps/ui/fragment/RFPSEnrollFragment.kt | 24 +- .../ui/viewmodel/RFPSIconTouchViewModel.kt | 10 +- .../rfps/ui/viewmodel/RFPSViewModel.kt | 39 +-- .../rfps/ui/widget/RFPSProgressBar.kt | 1 + .../udfps/ui/fragment/UdfpsEnrollFragment.kt | 26 +- .../ui/viewmodel/UdfpsLastStepViewModel.kt | 24 +- .../udfps/ui/viewmodel/UdfpsViewModel.kt | 110 +++---- .../UdfpsEnrollProgressBarDrawableV2.kt | 1 + .../udfps/ui/widget/UdfpsEnrollViewV2.kt | 10 +- .../viewmodel/BackgroundViewModel.kt | 9 +- .../FingerprintEnrollConfirmationViewModel.kt | 31 +- .../FingerprintEnrollEnrollingViewModel.kt | 32 ++- .../FingerprintEnrollFindSensorViewModel.kt | 65 +++-- .../FingerprintEnrollIntroViewModel.kt | 38 +-- .../viewmodel/FingerprintEnrollViewModel.kt | 56 +++- .../viewmodel/FingerprintFlowViewModel.kt | 23 +- .../FingerprintGatekeeperViewModel.kt | 45 +-- .../FingerprintNavigationViewModel.kt | 82 +++--- .../viewmodel/FingerprintScrollViewModel.kt | 10 +- .../viewmodel/TransitionViewModel.kt | 17 +- .../binder/FingerprintSettingsViewBinder.kt | 11 + .../fragment/FingerprintSettingsV2Fragment.kt | 4 +- .../FingerprintSettingsNavigationViewModel.kt | 4 +- .../viewmodel/FingerprintSettingsViewModel.kt | 2 +- .../FingerprintEnrollIntroFragmentTest.kt | 11 +- ...ation.png => rfps_enroll_confirmation.png} | Bin ...nrolling.png => rfps_enroll_enrolling.png} | Bin ...sensor.png => rfps_enroll_find_sensor.png} | Bin ...enroll_intro.png => rfps_enroll_intro.png} | Bin .../biometrics/fingerprint/Injector.kt | 37 ++- ...> RfpsEnrollConfirmationScreenshotTest.kt} | 9 +- ...t => RfpsEnrollEnrollingScreenshotTest.kt} | 6 +- .../RfpsEnrollFindSensorScreenshotTest.kt | 6 +- ...st.kt => RfpsEnrollIntroScreenshotTest.kt} | 8 +- .../FakeFingerprintManagerInteractor.kt | 2 + .../FingerprintManagerInteractorTest.kt | 27 +- ...gerprintEnrollFindSensorViewModelV2Test.kt | 74 ++--- ...gerprintEnrollConfirmationViewModelTest.kt | 87 ++++-- ...FingerprintEnrollEnrollingViewModelTest.kt | 48 ++-- 68 files changed, 1068 insertions(+), 939 deletions(-) create mode 100644 src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt rename src/com/android/settings/biometrics/fingerprint2/{ => debug}/data/repository/UdfpsEnrollDebugRepository.kt (66%) create mode 100644 src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt create mode 100644 src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt create mode 100644 src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt rename tests/screenshot/assets/robolectric/{fp_enroll_confirmation.png => rfps_enroll_confirmation.png} (100%) rename tests/screenshot/assets/robolectric/{fp_enroll_enrolling.png => rfps_enroll_enrolling.png} (100%) rename tests/screenshot/assets/robolectric/{fp_enroll_find_sensor.png => rfps_enroll_find_sensor.png} (100%) rename tests/screenshot/assets/robolectric/{fp_enroll_intro.png => rfps_enroll_intro.png} (100%) rename tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/{FingerprintEnrollConfirmationScreenshotTest.kt => RfpsEnrollConfirmationScreenshotTest.kt} (86%) rename tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/{FingerprintEnrollEnrollingScreenshotTest.kt => RfpsEnrollEnrollingScreenshotTest.kt} (86%) rename tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/{FingerprintEnrollIntroScreenshotTest.kt => RfpsEnrollIntroScreenshotTest.kt} (89%) diff --git a/Android.bp b/Android.bp index b96d0dc2928..cb898bef834 100644 --- a/Android.bp +++ b/Android.bp @@ -108,6 +108,8 @@ android_library { "telephony_flags_core_java_lib", "setupdesign-lottie-loading-layout", "device_policy_aconfig_flags_lib", + "kotlinx-coroutines-core", + "kotlinx-coroutines-android", ], plugins: ["androidx.room_room-compiler-plugin"], diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java index 169e0462545..7d5d2c74d97 100644 --- a/src/com/android/settings/SettingsApplication.java +++ b/src/com/android/settings/SettingsApplication.java @@ -24,9 +24,11 @@ import android.provider.Settings; import android.util.FeatureFlagUtils; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingUtils; +import com.android.settings.biometrics.fingerprint2.BiometricsEnvironment; import com.android.settings.core.instrumentation.ElapsedTimeUtils; import com.android.settings.development.DeveloperOptionsActivityLifecycle; import com.android.settings.fuelgauge.BatterySettingsStorage; @@ -47,6 +49,7 @@ import java.lang.ref.WeakReference; public class SettingsApplication extends Application { private WeakReference mHomeActivity = new WeakReference<>(null); + private BiometricsEnvironment mBiometricsEnvironment; @Override protected void attachBaseContext(Context base) { @@ -70,6 +73,7 @@ public class SettingsApplication extends Application { // Set Spa environment. setSpaEnvironment(); + mBiometricsEnvironment = new BiometricsEnvironment(this); if (ActivityEmbeddingUtils.isSettingsSplitEnabled(this) && FeatureFlagUtils.isEnabled(this, @@ -111,6 +115,11 @@ public class SettingsApplication extends Application { return mHomeActivity.get(); } + @Nullable + public BiometricsEnvironment getBiometricEnvironment() { + return mBiometricsEnvironment; + } + @Override public void onTrimMemory(int level) { super.onTrimMemory(level); diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java index f292aae9dfc..8d1113ee289 100644 --- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollHelper.java @@ -184,7 +184,6 @@ public class UdfpsEnrollHelper extends InstrumentedFragment { */ public void onAcquired(boolean isAcquiredGood) { if (mListener != null) { - Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep()); mListener.onAcquired(isAcquiredGood && animateIfLastStep()); } } diff --git a/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt new file mode 100644 index 00000000000..215692ab0e1 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/BiometricsEnvironment.kt @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint2 + +import android.hardware.fingerprint.FingerprintManager +import android.view.MotionEvent +import android.view.accessibility.AccessibilityManager +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.ViewModelStoreOwner +import com.android.internal.widget.LockPatternUtils +import com.android.settings.SettingsApplication +import com.android.settings.biometrics.GatekeeperPasswordProvider +import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepository +import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl +import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository +import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl +import com.android.settings.biometrics.fingerprint2.debug.data.repository.UdfpsEnrollDebugRepositoryImpl +import com.android.settings.biometrics.fingerprint2.debug.domain.interactor.DebugTouchEventInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl +import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor +import com.android.settings.biometrics.fingerprint2.lib.model.Settings +import java.util.concurrent.Executors +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +/** + * This class should handle all repo & interactor creation needed by the ViewModels for the + * biometrics code. + * + * This code is instantiated within the [SettingsApplication], all repos should be private & + * immutable and all interactors should public and immutable + */ +class BiometricsEnvironment(context: SettingsApplication) : ViewModelStoreOwner { + + private val executorService = Executors.newSingleThreadExecutor() + private val backgroundDispatcher = executorService.asCoroutineDispatcher() + private val applicationScope = MainScope() + private val gateKeeperPasswordProvider = GatekeeperPasswordProvider(LockPatternUtils(context)) + private val fingerprintManager = + context.getSystemService(FragmentActivity.FINGERPRINT_SERVICE) as FingerprintManager? + + private val fingerprintSensorRepository: FingerprintSensorRepository = + FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, applicationScope) + private val debuggingRepository: DebuggingRepository = DebuggingRepositoryImpl() + private val udfpsDebugRepo = UdfpsEnrollDebugRepositoryImpl() + + /** For now, interactors are public to those with access to the [BiometricsEnvironment] class */ + val fingerprintEnrollInteractor: FingerprintEnrollInteractor by lazy { + FingerprintEnrollInteractorImpl(context, fingerprintManager, Settings) + } + + /** [FingerprintManagerInteractor] to be used to construct view models */ + val fingerprintManagerInteractor: FingerprintManagerInteractor by lazy { + FingerprintManagerInteractorImpl( + context, + backgroundDispatcher, + fingerprintManager, + fingerprintSensorRepository, + gateKeeperPasswordProvider, + fingerprintEnrollInteractor, + ) + } + + val accessibilityInteractor: AccessibilityInteractor by lazy { + AccessibilityInteractorImpl( + context.getSystemService(AccessibilityManager::class.java)!!, + applicationScope, + ) + } + + val foldStateInteractor: FoldStateInteractor by lazy { FoldStateInteractorImpl(context) } + + val orientationInteractor: OrientationInteractor by lazy { OrientationInteractorImpl(context) } + + val vibrationInteractor: VibrationInteractor by lazy { VibrationInteractorImpl(context) } + + val displayDensityInteractor: DisplayDensityInteractor by lazy { + DisplayDensityInteractorImpl(context, applicationScope) + } + + val debuggingInteractor: DebuggingInteractor by lazy { + DebuggingInteractorImpl(debuggingRepository) + } + + val enrollStageInteractor: EnrollStageInteractor by lazy { EnrollStageInteractorImpl() } + + val udfpsEnrollInteractor: UdfpsEnrollInteractor by lazy { + UdfpsEnrollInteractorImpl(context, accessibilityInteractor) + } + + val sensorInteractor: FingerprintSensorInteractor by lazy { + FingerprintSensorInteractorImpl(fingerprintSensorRepository) + } + + val touchEventInteractor: TouchEventInteractor by lazy { + if (debuggingRepository.isDebuggingEnabled()) { + DebugTouchEventInteractorImpl(udfpsDebugRepo) + } else { + object : TouchEventInteractor { + override val touchEvent: Flow = flowOf() + } + } + } + + override val viewModelStore: ViewModelStore = ViewModelStore() +} diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt index d007e1a1b08..60838091b21 100644 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt +++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/DebuggingRepository.kt @@ -23,6 +23,7 @@ interface DebuggingRepository { /** A function that will return if a build is debuggable */ fun isDebuggingEnabled(): Boolean + /** A function that will return if udfps enrollment should be swapped with debug repos */ fun isUdfpsEnrollmentDebuggingEnabled(): Boolean } diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt index b7616e4c4b3..516549e65f4 100644 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt +++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt @@ -46,7 +46,7 @@ interface FingerprintSensorRepository { } class FingerprintSensorRepositoryImpl( - fingerprintManager: FingerprintManager, + fingerprintManager: FingerprintManager?, backgroundDispatcher: CoroutineDispatcher, activityScope: CoroutineScope, ) : FingerprintSensorRepository { @@ -73,12 +73,9 @@ class FingerprintSensorRepositoryImpl( .stateIn(activityScope, started = SharingStarted.Eagerly, initialValue = DEFAULT_PROPS) override val fingerprintSensor: Flow = - fingerprintPropsInternal.transform { - emit(it.toFingerprintSensor()) - } + fingerprintPropsInternal.transform { emit(it.toFingerprintSensor()) } companion object { - private const val TAG = "FingerprintSensorRepoImpl" private val DEFAULT_PROPS = FingerprintSensorPropertiesInternal( diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt index a3bcb1210b5..3c355e76853 100644 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt +++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/SimulatedTouchEventsRepository.kt @@ -17,6 +17,7 @@ package com.android.settings.biometrics.fingerprint2.data.repository import android.graphics.Point +import android.view.MotionEvent import kotlinx.coroutines.flow.Flow /** @@ -24,8 +25,6 @@ import kotlinx.coroutines.flow.Flow * that talkback is correct. */ interface SimulatedTouchEventsRepository { - /** - * A flow simulating user touches. - */ - val touchExplorationDebug: Flow + /** A flow simulating user touches. */ + val touchExplorationDebug: Flow } diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/UdfpsEnrollDebugRepository.kt b/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt similarity index 66% rename from src/com/android/settings/biometrics/fingerprint2/data/repository/UdfpsEnrollDebugRepository.kt rename to src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt index 9b74813e33f..0c3152a83ae 100644 --- a/src/com/android/settings/biometrics/fingerprint2/data/repository/UdfpsEnrollDebugRepository.kt +++ b/src/com/android/settings/biometrics/fingerprint2/debug/data/repository/UdfpsEnrollDebugRepository.kt @@ -14,10 +14,14 @@ * limitations under the License. */ -package com.android.settings.biometrics.fingerprint2.data.repository +package com.android.settings.biometrics.fingerprint2.debug.data.repository -import android.graphics.Point import android.graphics.Rect +import android.hardware.fingerprint.FingerprintEnrollOptions +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_HOVER_MOVE +import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository +import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractor import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState @@ -36,7 +40,11 @@ import kotlinx.coroutines.flow.flowOf class UdfpsEnrollDebugRepositoryImpl : FingerprintEnrollInteractor, FingerprintSensorRepository, SimulatedTouchEventsRepository { - override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason) = flow { + override suspend fun enroll( + hardwareAuthToken: ByteArray?, + enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions, + ) = flow { emit(FingerEnrollState.OverlayShown) delay(200) emit(FingerEnrollState.EnrollHelp(helpMsgId, "Hello world")) @@ -77,7 +85,7 @@ class UdfpsEnrollDebugRepositoryImpl : } /** Provides touch events to the UdfpsEnrollFragment */ - override val touchExplorationDebug: Flow = flow { + override val touchExplorationDebug: Flow = flow { delay(2000) emit(pointToLeftOfSensor(sensorRect)) delay(2000) @@ -90,17 +98,45 @@ class UdfpsEnrollDebugRepositoryImpl : override val fingerprintSensor: Flow = flowOf(sensorProps) - private fun pointToLeftOfSensor(sensorLocation: Rect) = - Point(sensorLocation.right + 5, sensorLocation.centerY()) + private fun pointToLeftOfSensor(sensorLocation: Rect): MotionEvent = + MotionEvent.obtain( + 100, + 100, + ACTION_HOVER_MOVE, + sensorLocation.right + 5.0f, + sensorLocation.centerY().toFloat(), + 0, + ) - private fun pointToRightOfSensor(sensorLocation: Rect) = - Point(sensorLocation.left - 5, sensorLocation.centerY()) + private fun pointToRightOfSensor(sensorLocation: Rect): MotionEvent = + MotionEvent.obtain( + 100, + 100, + ACTION_HOVER_MOVE, + sensorLocation.right - 5.0f, + sensorLocation.centerY().toFloat(), + 0, + ) - private fun pointBelowSensor(sensorLocation: Rect) = - Point(sensorLocation.centerX(), sensorLocation.bottom + 5) + private fun pointBelowSensor(sensorLocation: Rect): MotionEvent = + MotionEvent.obtain( + 100, + 100, + ACTION_HOVER_MOVE, + sensorLocation.centerX().toFloat(), + sensorLocation.bottom + 5.0f, + 0, + ) - private fun pointAboveSensor(sensorLocation: Rect) = - Point(sensorLocation.centerX(), sensorLocation.top - 5) + private fun pointAboveSensor(sensorLocation: Rect): MotionEvent = + MotionEvent.obtain( + 100, + 100, + ACTION_HOVER_MOVE, + sensorLocation.centerX().toFloat(), + sensorLocation.top - 5.0f, + 0, + ) companion object { @@ -109,10 +145,10 @@ class UdfpsEnrollDebugRepositoryImpl : private val sensorRadius = 100 private val sensorRect = Rect( - this.sensorLocationInternal.first - sensorRadius, - this.sensorLocationInternal.second - sensorRadius, - this.sensorLocationInternal.first + sensorRadius, - this.sensorLocationInternal.second + sensorRadius, + sensorLocationInternal.first - sensorRadius, + sensorLocationInternal.second - sensorRadius, + sensorLocationInternal.first + sensorRadius, + sensorLocationInternal.second + sensorRadius, ) val sensorProps = FingerprintSensor( diff --git a/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt new file mode 100644 index 00000000000..fff6b6609fd --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/debug/domain/interactor/DebugTouchEventInteractorImpl.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint2.debug.domain.interactor + +import android.view.MotionEvent +import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository +import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor +import kotlinx.coroutines.flow.Flow + +class DebugTouchEventInteractorImpl( + udfpsSimulatedTouchEventsRepository: SimulatedTouchEventsRepository +) : TouchEventInteractor { + override val touchEvent: Flow = + udfpsSimulatedTouchEventsRepository.touchExplorationDebug +} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt index e7692372a31..e1a08e6cda2 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt @@ -17,7 +17,7 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor import android.view.accessibility.AccessibilityManager -import androidx.lifecycle.LifecycleCoroutineScope +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted @@ -26,27 +26,27 @@ import kotlinx.coroutines.flow.stateIn /** Represents all of the information on accessibility state. */ interface AccessibilityInteractor { - /** A flow that contains whether or not accessibility is enabled */ - val isAccessibilityEnabled: Flow + /** A flow that contains whether or not accessibility is enabled */ + val isAccessibilityEnabled: Flow } class AccessibilityInteractorImpl( - accessibilityManager: AccessibilityManager, - activityScope: LifecycleCoroutineScope + accessibilityManager: AccessibilityManager, + applicationScope: CoroutineScope, ) : AccessibilityInteractor { /** A flow that contains whether or not accessibility is enabled */ override val isAccessibilityEnabled: Flow = callbackFlow { val listener = - AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) } + AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) } accessibilityManager.addAccessibilityStateChangeListener(listener) // This clause will be called when no one is listening to the flow awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) } - } + } .stateIn( - activityScope, // This is going to tied to the activity scope - SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener - false + applicationScope, // This is going to tied to the activity scope + SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener + false, ) -} \ No newline at end of file +} diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DebuggingInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DebuggingInteractor.kt index 3edca96b4a2..d0a056d33a5 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DebuggingInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DebuggingInteractor.kt @@ -28,9 +28,7 @@ interface DebuggingInteractor { val udfpsEnrollmentDebuggingEnabled: Flow } -/** - * This interactor essentially forwards the [DebuggingRepository] - */ +/** This interactor essentially forwards the [DebuggingRepository] */ class DebuggingInteractorImpl(val debuggingRepository: DebuggingRepository) : DebuggingInteractor { override val debuggingEnabled: Flow = flow { emit(debuggingRepository.isDebuggingEnabled()) diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DisplayDensityInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DisplayDensityInteractor.kt index 67c00017136..6dfb4c290d6 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DisplayDensityInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/DisplayDensityInteractor.kt @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor +import android.content.Context +import com.android.settingslib.display.DisplayDensityUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -50,12 +52,11 @@ interface DisplayDensityInteractor { * Implementation of the [DisplayDensityInteractor]. This interactor is used to forward activity * information to the rest of the application. */ -class DisplayDensityInteractorImpl( - currentFontScale: Float, - currentDisplayDensity: Int, - defaultDisplayDensity: Int, - scope: CoroutineScope, -) : DisplayDensityInteractor { +class DisplayDensityInteractorImpl(context: Context, scope: CoroutineScope) : + DisplayDensityInteractor { + + val displayDensityUtils = DisplayDensityUtils(context) + override fun updateDisplayDensity(density: Int) { _displayDensity.update { density } } @@ -64,13 +65,18 @@ class DisplayDensityInteractorImpl( _fontScale.update { fontScale } } - private val _fontScale = MutableStateFlow(currentFontScale) - private val _displayDensity = MutableStateFlow(currentDisplayDensity) + private val _fontScale = MutableStateFlow(context.resources.configuration.fontScale) + private val _displayDensity = + MutableStateFlow( + displayDensityUtils.defaultDisplayDensityValues[ + displayDensityUtils.currentIndexForDefaultDisplay] + ) override val fontScale: Flow = _fontScale.asStateFlow() override val displayDensity: Flow = _displayDensity.asStateFlow() override val defaultDisplayDensity: Flow = - flowOf(defaultDisplayDensity).shareIn(scope, SharingStarted.Eagerly, 1) + flowOf(displayDensityUtils.defaultDensityForDefaultDisplay) + .shareIn(scope, SharingStarted.Eagerly, 1) } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt index f967e04f936..a36832db076 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintEnrollInteractor.kt @@ -33,7 +33,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.update /** This repository is responsible for collecting all state related to the enroll API. */ @@ -45,13 +44,13 @@ interface FingerprintEnrollInteractor { suspend fun enroll( hardwareAuthToken: ByteArray?, enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions, ): Flow } class FingerprintEnrollInteractorImpl( private val applicationContext: Context, - private val fingerprintEnrollOptions: FingerprintEnrollOptions, - private val fingerprintManager: FingerprintManager, + private val fingerprintManager: FingerprintManager?, private val fingerprintFlow: FingerprintFlow, ) : FingerprintEnrollInteractor { private val enrollRequestOutstanding = MutableStateFlow(false) @@ -59,6 +58,7 @@ class FingerprintEnrollInteractorImpl( override suspend fun enroll( hardwareAuthToken: ByteArray?, enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions, ): Flow = callbackFlow { // TODO (b/308456120) Improve this logic if (enrollRequestOutstanding.value) { @@ -135,7 +135,7 @@ class FingerprintEnrollInteractorImpl( val cancellationSignal = CancellationSignal() - fingerprintManager.enroll( + fingerprintManager?.enroll( hardwareAuthToken, cancellationSignal, applicationContext.userId, 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 ca3665ce8ca..f03c94ea047 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt @@ -18,6 +18,7 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor import android.content.Context import android.content.Intent +import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback import android.hardware.fingerprint.FingerprintManager.RemovalCallback @@ -45,7 +46,7 @@ private const val TAG = "FingerprintManagerInteractor" class FingerprintManagerInteractorImpl( applicationContext: Context, private val backgroundDispatcher: CoroutineDispatcher, - private val fingerprintManager: FingerprintManager, + private val fingerprintManager: FingerprintManager?, fingerprintSensorRepository: FingerprintSensorRepository, private val gatekeeperPasswordProvider: GatekeeperPasswordProvider, private val fingerprintEnrollStateRepository: FingerprintEnrollInteractor, @@ -57,7 +58,6 @@ class FingerprintManagerInteractorImpl( ) private val applicationContext = applicationContext.applicationContext - override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair = suspendCoroutine { val callback = GenerateChallengeCallback { _, userId, challenge -> @@ -70,21 +70,19 @@ class FingerprintManagerInteractorImpl( val p = Pair(challenge, challengeToken) it.resume(p) } - fingerprintManager.generateChallenge(applicationContext.userId, callback) + fingerprintManager?.generateChallenge(applicationContext.userId, callback) } - override val enrolledFingerprints: Flow> = flow { + override val enrolledFingerprints: Flow?> = flow { emit( - fingerprintManager - .getEnrolledFingerprints(applicationContext.userId) - .map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) } - .toList() + fingerprintManager?.getEnrolledFingerprints(applicationContext.userId) + ?.map { (FingerprintData(it.name.toString(), it.biometricId, it.deviceId)) }?.toList() ) } override val canEnrollFingerprints: Flow = flow { emit( - fingerprintManager.getEnrolledFingerprints(applicationContext.userId).size < maxFingerprints + fingerprintManager?.getEnrolledFingerprints(applicationContext.userId)?.size ?: maxFingerprints < maxFingerprints ) } @@ -92,8 +90,16 @@ class FingerprintManagerInteractorImpl( override val maxEnrollableFingerprints = flow { emit(maxFingerprints) } - override suspend fun enroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason): Flow = - fingerprintEnrollStateRepository.enroll(hardwareAuthToken, enrollReason) + override suspend fun enroll( + hardwareAuthToken: ByteArray?, + enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions, + ): Flow = + fingerprintEnrollStateRepository.enroll( + hardwareAuthToken, + enrollReason, + fingerprintEnrollOptions, + ) override suspend fun removeFingerprint(fp: FingerprintData): Boolean = suspendCoroutine { val callback = @@ -113,7 +119,7 @@ class FingerprintManagerInteractorImpl( it.resume(true) } } - fingerprintManager.remove( + fingerprintManager?.remove( android.hardware.fingerprint.Fingerprint(fp.name, fp.fingerId, fp.deviceId), applicationContext.userId, callback, @@ -122,12 +128,12 @@ class FingerprintManagerInteractorImpl( override suspend fun renameFingerprint(fp: FingerprintData, newName: String) { withContext(backgroundDispatcher) { - fingerprintManager.rename(fp.fingerId, applicationContext.userId, newName) + fingerprintManager?.rename(fp.fingerId, applicationContext.userId, newName) } } - override suspend fun hasSideFps(): Boolean = suspendCancellableCoroutine { - it.resume(fingerprintManager.isPowerbuttonFps) + override suspend fun hasSideFps(): Boolean? = suspendCancellableCoroutine { + it.resume(fingerprintManager?.isPowerbuttonFps) } override suspend fun authenticate(): FingerprintAuthAttemptModel = @@ -156,7 +162,7 @@ class FingerprintManagerInteractorImpl( val cancellationSignal = CancellationSignal() c.invokeOnCancellation { cancellationSignal.cancel() } - fingerprintManager.authenticate( + fingerprintManager?.authenticate( null, cancellationSignal, authenticationCallback, @@ -164,5 +170,4 @@ class FingerprintManagerInteractorImpl( applicationContext.userId, ) } - } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt new file mode 100644 index 00000000000..073629c6537 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintSensorInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint2.domain.interactor + +import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository +import com.android.systemui.biometrics.shared.model.FingerprintSensor +import kotlinx.coroutines.flow.Flow + +/** + * Interactor that propagates the type of [FingerprintSensor] this device supports. + */ +interface FingerprintSensorInteractor { + /** Get the [FingerprintSensor] */ + val fingerprintSensor: Flow +} + +class FingerprintSensorInteractorImpl(repo: FingerprintSensorRepository) : + FingerprintSensorInteractor { + override val fingerprintSensor: Flow = repo.fingerprintSensor +} diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt index 0224aa2ef93..51de882093b 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FoldStateInteractor.kt @@ -25,34 +25,30 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow interface FoldStateInteractor { - /** A flow that contains the fold state info */ - val isFolded: Flow + /** 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) + /** + * Indicates a configuration change has occurred, and the repo should update the [isFolded] flow. + */ + fun onConfigurationChange(newConfig: Configuration) } -/** - * Interactor which handles fold state - */ +/** Interactor which handles fold state */ class FoldStateInteractorImpl(context: Context) : FoldStateInteractor { - private val screenSizeFoldProvider = ScreenSizeFoldProvider(context) - override val isFolded: Flow = callbackFlow { - val foldStateListener = FoldProvider.FoldCallback { isFolded -> trySend(isFolded) } - screenSizeFoldProvider.registerCallback(foldStateListener, context.mainExecutor) - awaitClose { screenSizeFoldProvider.unregisterCallback(foldStateListener) } - } - - /** - * This function is called by the root activity, indicating an orientation event has occurred. - * When this happens, the [ScreenSizeFoldProvider] is notified and it will re-compute if the - * device is folded or not, and notify the [FoldProvider.FoldCallback] - */ - override fun onConfigurationChange(newConfig: Configuration) { - screenSizeFoldProvider.onConfigurationChange(newConfig) - } + private val screenSizeFoldProvider = ScreenSizeFoldProvider(context) + override val isFolded: Flow = 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/domain/interactor/OrientationInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt index 3ecf3121158..e55d6b899ca 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt @@ -60,10 +60,7 @@ class OrientationInteractorImpl(private val context: Context) : OrientationInter awaitClose { orientationEventListener.disable() } } - override val rotation: Flow = - orientation.transform { - emit(context.display!!.rotation) - } + override val rotation: Flow = orientation.transform { emit(context.display.rotation) } override val rotationFromDefault: Flow = rotation.map { getRotationFromDefault(it) } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt index ab7b5de3f51..6fb7555358e 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/PressToAuthInteractor.kt @@ -28,77 +28,67 @@ import kotlinx.coroutines.flow.flowOn /** Interface that indicates if press to auth is on or off. */ interface PressToAuthInteractor { - /** Indicates true if the PressToAuth feature is enabled, false otherwise. */ - val isEnabled: Flow + /** 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, + 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) + /** 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()) } - }.flowOn(backgroundDispatcher) + } + context.contentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED), + false, + callback, + context.userId, + ) + trySend(getPressToAuth()) + awaitClose { context.contentResolver.unregisterContentObserver(callback) } + } + .flowOn(backgroundDispatcher) - /** - * Returns true if press to auth is enabled - */ - private fun getPressToAuth(): Boolean { - var toReturn: Int = - Settings.Secure.getIntForUser( - context.contentResolver, - Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, - -1, - context.userId, - ) - if (toReturn == -1) { - toReturn = - if ( - context.resources.getBoolean(com.android.internal.R.bool.config_performantAuthDefault) - ) { - 1 - } else { - 0 - } - Settings.Secure.putIntForUser( - context.contentResolver, - Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, - toReturn, - context.userId, - ) + /** Returns true if press to auth is enabled */ + private fun getPressToAuth(): Boolean { + var toReturn: Int = + Settings.Secure.getIntForUser( + context.contentResolver, + Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, + -1, + context.userId, + ) + if (toReturn == -1) { + toReturn = + if ( + context.resources.getBoolean(com.android.internal.R.bool.config_performantAuthDefault) + ) { + 1 + } else { + 0 } - return toReturn == 1 - + Settings.Secure.putIntForUser( + context.contentResolver, + Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED, + toReturn, + context.userId, + ) } + return toReturn == 1 + } - companion object { - const val TAG = "PressToAuthInteractor" - } + companion object { + const val TAG = "PressToAuthInteractor" + } } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt new file mode 100644 index 00000000000..4ef2afa0875 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/TouchEventInteractor.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint2.domain.interactor + +import android.view.MotionEvent +import kotlinx.coroutines.flow.Flow + +interface TouchEventInteractor { + + /** A flow simulating user touches. */ + val touchEvent: Flow +} + diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt index 107d1f65459..006060a54b9 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/UdfpsEnrollInteractor.kt @@ -16,8 +16,9 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor +import android.content.Context import android.graphics.PointF -import android.util.Log +import android.util.TypedValue import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -43,13 +44,18 @@ interface UdfpsEnrollInteractor { /** Keeps track of which guided enrollment point we should be using */ class UdfpsEnrollInteractorImpl( - pixelsPerMillimeter: Float, + applicationContext: Context, accessibilityInteractor: AccessibilityInteractor, ) : UdfpsEnrollInteractor { private var isGuidedEnrollment = MutableStateFlow(false) // Number of pixels per mm - val px = pixelsPerMillimeter + val px = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_MM, + 1f, + applicationContext.resources.displayMetrics, + ) private val guidedEnrollmentPoints: MutableList = mutableListOf( PointF(2.00f * px, 0.00f * px), @@ -70,7 +76,6 @@ class UdfpsEnrollInteractorImpl( override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) { val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size - Log.e("JRM", "guided enroll step $index") _guidedEnrollment.update { guidedEnrollmentPoints[index] } } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/VibrationInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/VibrationInteractor.kt index 0f107cc6e36..97a68f0cf36 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/VibrationInteractor.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/VibrationInteractor.kt @@ -36,6 +36,7 @@ sealed class FingerprintVibrationEffects { /** This vibration typically occurs when a help message is shown during UDFPS enrollment */ data object UdfpsHelp : FingerprintVibrationEffects() } + /** Interface for sending haptic feedback */ interface VibrationInteractor { /** This will send a haptic vibration */ @@ -43,8 +44,9 @@ interface VibrationInteractor { } /** Implementation of the VibrationInteractor interface */ -class VibrationInteractorImpl(val vibrator: Vibrator, val applicationContext: Context) : - VibrationInteractor { +class VibrationInteractorImpl(val applicationContext: Context) : VibrationInteractor { + val vibrator = applicationContext.getSystemService(Vibrator::class.java)!! + override fun vibrate(effect: FingerprintVibrationEffects, caller: String) { val callerString = "$caller::$effect" val res = 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 d2f9f0ae014..5f4cecabab1 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 @@ -16,7 +16,7 @@ package com.android.settings.biometrics.fingerprint2.lib.domain.interactor -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal +import android.hardware.fingerprint.FingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.Flow */ interface FingerprintManagerInteractor { /** Returns the list of current fingerprints. */ - val enrolledFingerprints: Flow> + val enrolledFingerprints: Flow?> /** Returns the max enrollable fingerprints, note during SUW this might be 1 */ val maxEnrollableFingerprints: Flow @@ -62,6 +62,7 @@ interface FingerprintManagerInteractor { suspend fun enroll( hardwareAuthToken: ByteArray?, enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions, ): Flow /** @@ -74,5 +75,5 @@ interface FingerprintManagerInteractor { suspend fun renameFingerprint(fp: FingerprintData, newName: String) /** Indicates if the device has side fingerprint */ - suspend fun hasSideFps(): Boolean + suspend fun hasSideFps(): Boolean? } diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/model/EnrollReason.kt b/src/com/android/settings/biometrics/fingerprint2/lib/model/EnrollReason.kt index 3cc6497014e..02df834049a 100644 --- a/src/com/android/settings/biometrics/fingerprint2/lib/model/EnrollReason.kt +++ b/src/com/android/settings/biometrics/fingerprint2/lib/model/EnrollReason.kt @@ -24,5 +24,5 @@ enum class EnrollReason { */ FindSensor, /** The enroll happens on enrolling screen. */ - EnrollEnrolling + EnrollEnrolling, } 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 d25fcd06e36..421548fabf9 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 @@ -22,16 +22,12 @@ import android.content.res.Configuration import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintManager import android.os.Bundle -import android.os.Vibrator import android.util.Log -import android.util.TypedValue -import android.view.accessibility.AccessibilityManager import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import com.android.internal.widget.LockPatternUtils import com.android.settings.R import com.android.settings.SetupWizardUtils import com.android.settings.Utils.SETTINGS_PACKAGE_NAME @@ -40,27 +36,9 @@ import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED import com.android.settings.biometrics.BiometricUtils import com.android.settings.biometrics.GatekeeperPasswordProvider -import com.android.settings.biometrics.fingerprint2.data.repository.DebuggingRepositoryImpl -import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl -import com.android.settings.biometrics.fingerprint2.data.repository.UdfpsEnrollDebugRepositoryImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractorImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor -import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractorImpl import com.android.settings.biometrics.fingerprint2.lib.model.Default -import com.android.settings.biometrics.fingerprint2.lib.model.Settings import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment @@ -68,18 +46,12 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.RfpsEnrollFindSensorFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.SfpsEnrollFindSensorFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.education.UdfpsEnrollFindSensorFragment -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollIntroViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel @@ -91,7 +63,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Transition import com.android.settings.flags.Flags @@ -113,23 +84,38 @@ private const val TAG = "FingerprintEnrollmentV2Activity" * children fragments. */ class FingerprintEnrollmentV2Activity : FragmentActivity() { - private lateinit var fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel - private lateinit var navigationViewModel: FingerprintNavigationViewModel - private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel - private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel - private lateinit var vibrationInteractor: VibrationInteractor + private val navigationViewModel: FingerprintNavigationViewModel by viewModels { + FingerprintNavigationViewModel.Factory + } + private val fingerprintFlowViewModel: FingerprintFlowViewModel by viewModels { + FingerprintFlowViewModel.Factory + } + + private val gatekeeperViewModel: FingerprintGatekeeperViewModel by viewModels { + FingerprintGatekeeperViewModel.Factory + } + + /** + * View models below this line are not used by this class but must be initialized + * in the activity view model store to be used by other view models. + */ + private val fingerprintEnrollViewModel: FingerprintEnrollViewModel by viewModels { + FingerprintEnrollViewModel.Factory + } + + private val fingerprintEnrollEnrollingViewModel: + FingerprintEnrollEnrollingViewModel by viewModels { + FingerprintEnrollEnrollingViewModel.Factory + } + + private val udfpsLastStepViewModel: UdfpsLastStepViewModel by viewModels { + UdfpsLastStepViewModel.Factory + } + + private val backgroundViewModel: BackgroundViewModel by viewModels { BackgroundViewModel.Factory } + private lateinit var foldStateInteractor: FoldStateInteractor - private lateinit var orientationInteractor: OrientationInteractor private lateinit var displayDensityInteractor: DisplayDensityInteractor - private lateinit var udfpsEnrollInteractor: UdfpsEnrollInteractor - private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel - private lateinit var backgroundViewModel: BackgroundViewModel - private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel - private lateinit var fingerprintEnrollConfirmationViewModel: - FingerprintEnrollConfirmationViewModel - private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel - private lateinit var udfpsViewModel: UdfpsViewModel - private lateinit var enrollStageInteractor: EnrollStageInteractor private val coroutineDispatcher = Dispatchers.Default /** Result listener for ChooseLock activity flow. */ @@ -172,7 +158,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private fun onConfirmDevice(resultCode: Int, data: Intent?) { val wasSuccessful = resultCode == RESULT_FINISHED || resultCode == Activity.RESULT_OK - val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long? + val gateKeeperPasswordHandle = data?.getExtra(EXTRA_KEY_GK_PW_HANDLE) as Long lifecycleScope.launch { val confirmDeviceResult = @@ -204,6 +190,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { finish() } + // Ensure that these view models are actually created and in this order + navigationViewModel + fingerprintFlowViewModel + gatekeeperViewModel + fingerprintEnrollViewModel + backgroundViewModel + fingerprintEnrollEnrollingViewModel + udfpsLastStepViewModel + setTheme(SetupWizardUtils.getTheme(applicationContext, intent)) ThemeHelper.trySetDynamicColor(applicationContext) @@ -219,31 +214,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { Default } - backgroundViewModel = - ViewModelProvider(this, BackgroundViewModel.BackgroundViewModelFactory())[ - BackgroundViewModel::class.java] - - fingerprintFlowViewModel = - ViewModelProvider(this, FingerprintFlowViewModel.FingerprintFlowViewModelFactory(enrollType))[ - FingerprintFlowViewModel::class.java] - val displayDensityUtils = DisplayDensityUtils(context) - val currIndex = displayDensityUtils.currentIndexForDefaultDisplay - val defaultDisplayDensity = displayDensityUtils.defaultDensityForDefaultDisplay - displayDensityInteractor = - DisplayDensityInteractorImpl( - resources.configuration.fontScale, - displayDensityUtils.defaultDisplayDensityValues[currIndex], - defaultDisplayDensity, - lifecycleScope, - ) - - val debuggingRepo = DebuggingRepositoryImpl() - val debuggingInteractor = DebuggingInteractorImpl(debuggingRepo) - val udfpsEnrollDebugRepositoryImpl = UdfpsEnrollDebugRepositoryImpl() - - val fingerprintSensorRepo = - if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl - else FingerprintSensorRepositoryImpl(fingerprintManager, backgroundDispatcher, lifecycleScope) + fingerprintFlowViewModel.updateFlowType(enrollType) if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) { val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent) @@ -254,170 +225,18 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { ) } - val fingerprintEnrollStateRepository = - if (debuggingRepo.isUdfpsEnrollmentDebuggingEnabled()) udfpsEnrollDebugRepositoryImpl - else - FingerprintEnrollInteractorImpl( - context.applicationContext, - intent.toFingerprintEnrollOptions(), - fingerprintManager, - Settings, - ) - val accessibilityInteractor = - AccessibilityInteractorImpl( - getSystemService(AccessibilityManager::class.java)!!, - lifecycleScope, - ) - - val pixelsPerMillimeter = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, context.resources.displayMetrics) - udfpsEnrollInteractor = UdfpsEnrollInteractorImpl(pixelsPerMillimeter, accessibilityInteractor) - - val fingerprintManagerInteractor = - FingerprintManagerInteractorImpl( - context, - backgroundDispatcher, - fingerprintManager, - fingerprintSensorRepo, - GatekeeperPasswordProvider(LockPatternUtils(context)), - fingerprintEnrollStateRepository, - ) - var challenge = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long? val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN) val gatekeeperInfo = FingerprintGatekeeperViewModel.toGateKeeperInfo(challenge, token) val hasConfirmedDeviceCredential = gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo - navigationViewModel = - ViewModelProvider( - this, - FingerprintNavigationViewModel.FingerprintNavigationViewModelFactory( - Init, - hasConfirmedDeviceCredential, - fingerprintFlowViewModel, - fingerprintManagerInteractor, - ), - )[FingerprintNavigationViewModel::class.java] - // Initialize FingerprintEnrollIntroViewModel - ViewModelProvider( - this, - FingerprintEnrollIntroViewModel.FingerprintEnrollIntoViewModelFactory( - navigationViewModel, - fingerprintFlowViewModel, - fingerprintManagerInteractor, - ), - )[FingerprintEnrollIntroViewModel::class.java] - - gatekeeperViewModel = - ViewModelProvider( - this, - FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory( - gatekeeperInfo, - fingerprintManagerInteractor, - ), - )[FingerprintGatekeeperViewModel::class.java] - - // Initialize FoldStateViewModel - foldStateInteractor = FoldStateInteractorImpl(context) - foldStateInteractor.onConfigurationChange(resources.configuration) - - orientationInteractor = OrientationInteractorImpl(context) - vibrationInteractor = - VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context) - - // Initialize FingerprintViewModel - fingerprintEnrollViewModel = - ViewModelProvider( - this, - FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory( - fingerprintManagerInteractor, - gatekeeperViewModel, - navigationViewModel, - ), - )[FingerprintEnrollViewModel::class.java] - - // Initialize scroll view model - fingerprintScrollViewModel = - ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[ - FingerprintScrollViewModel::class.java] - - // Initialize FingerprintEnrollEnrollingViewModel - fingerprintEnrollEnrollingViewModel = - ViewModelProvider( - this, - FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory( - fingerprintEnrollViewModel, - backgroundViewModel, - ), - )[FingerprintEnrollEnrollingViewModel::class.java] - - // Initialize FingerprintEnrollFindSensorViewModel - ViewModelProvider( - this, - FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory( - navigationViewModel, - fingerprintEnrollViewModel, - gatekeeperViewModel, - backgroundViewModel, - accessibilityInteractor, - foldStateInteractor, - orientationInteractor, - fingerprintFlowViewModel, - fingerprintManagerInteractor, - ), - )[FingerprintEnrollFindSensorViewModel::class.java] - - // Initialize RFPS View Model - ViewModelProvider( - this, - RFPSViewModel.RFPSViewModelFactory( - fingerprintEnrollEnrollingViewModel, - navigationViewModel, - orientationInteractor, - fingerprintManagerInteractor, - ), - )[RFPSViewModel::class.java] - - enrollStageInteractor = EnrollStageInteractorImpl() - - udfpsLastStepViewModel = - UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) - - udfpsViewModel = - ViewModelProvider( - this, - UdfpsViewModel.UdfpsEnrollmentFactory( - vibrationInteractor, - displayDensityInteractor, - navigationViewModel, - debuggingInteractor, - fingerprintEnrollEnrollingViewModel, - udfpsEnrollDebugRepositoryImpl, - enrollStageInteractor, - orientationInteractor, - backgroundViewModel, - fingerprintSensorRepo, - udfpsEnrollInteractor, - fingerprintManagerInteractor, - udfpsLastStepViewModel, - accessibilityInteractor, - ), - )[UdfpsViewModel::class.java] - - fingerprintEnrollConfirmationViewModel = - ViewModelProvider( - this, - FingerprintEnrollConfirmationViewModel.FingerprintEnrollConfirmationViewModelFactory( - navigationViewModel, - fingerprintManagerInteractor, - ), - )[FingerprintEnrollConfirmationViewModel::class.java] + navigationViewModel.updateFingerprintFlow(enrollType) + navigationViewModel.hasConfirmedDeviceCredential(hasConfirmedDeviceCredential) lifecycleScope.launch { navigationViewModel.currentStep.collect { step -> if (step is Init) { - Log.d(TAG, "FingerprintNav.init($step)") navigationViewModel.update(FingerprintAction.ACTIVITY_CREATED, Init::class, "$TAG#init") } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt index d8c1c93f910..00541f8d0a3 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt @@ -20,8 +20,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -39,31 +39,11 @@ import kotlinx.coroutines.launch * This page will display basic information about what a fingerprint can be used for and acts as the * final step of enrollment. */ -class FingerprintEnrollConfirmationV2Fragment() : +class FingerprintEnrollConfirmationV2Fragment(factory: ViewModelProvider.Factory? = null) : Fragment(R.layout.fingerprint_enroll_finish_base) { - companion object { - const val TAG = "FingerprintEnrollConfirmationV2Fragment" - } - - /** Used for testing purposes */ - private var factory: ViewModelProvider.Factory? = null - - @VisibleForTesting - constructor(theFactory: ViewModelProvider.Factory) : this() { - factory = theFactory - } - - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } - - private val viewModel: FingerprintEnrollConfirmationViewModel by lazy { - viewModelProvider[FingerprintEnrollConfirmationViewModel::class.java] + private val viewModel: FingerprintEnrollConfirmationViewModel by activityViewModels { + factory ?: FingerprintEnrollConfirmationViewModel.Factory } override fun onCreateView( @@ -106,4 +86,8 @@ class FingerprintEnrollConfirmationV2Fragment() : private fun onNextButtonClick(view: View?) { viewModel.onNextButtonClicked() } + + companion object { + const val TAG = "FingerprintEnrollConfirmationV2Fragment" + } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt index 53d0ddf1b0e..6c87dfd4afe 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt @@ -30,8 +30,9 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.ScrollView import android.widget.TextView -import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.android.settings.R @@ -74,37 +75,22 @@ private data class TextModel( * 2. How the data will be stored * 3. How the user can access and remove their data */ -class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enroll_introduction) { - - /** Used for testing purposes */ - private var factory: ViewModelProvider.Factory? = null - - @VisibleForTesting - constructor(theFactory: ViewModelProvider.Factory) : this() { - factory = theFactory - } - - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } +class FingerprintEnrollIntroV2Fragment(testFactory: ViewModelProvider.Factory? = null) : + Fragment(R.layout.fingerprint_v2_enroll_introduction) { private lateinit var footerBarMixin: FooterBarMixin private lateinit var textModel: TextModel - private val viewModel: FingerprintEnrollIntroViewModel by lazy { - viewModelProvider[FingerprintEnrollIntroViewModel::class.java] + private val viewModel: FingerprintEnrollIntroViewModel by activityViewModels { + testFactory ?: FingerprintEnrollIntroViewModel.Factory } - private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy { - viewModelProvider[FingerprintScrollViewModel::class.java] + private val fingerprintScrollViewModel: FingerprintScrollViewModel by viewModels { + testFactory ?: FingerprintScrollViewModel.Factory } - private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy { - viewModelProvider[FingerprintGatekeeperViewModel::class.java] + private val gateKeeperViewModel: FingerprintGatekeeperViewModel by activityViewModels { + testFactory ?: FingerprintGatekeeperViewModel.Factory } override fun onCreateView( diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/RfpsEnrollFindSensorFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/RfpsEnrollFindSensorFragment.kt index 5ef17708d05..f1c1e344c1e 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/RfpsEnrollFindSensorFragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/RfpsEnrollFindSensorFragment.kt @@ -23,11 +23,15 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.android.settings.R import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterButton @@ -51,18 +55,10 @@ class RfpsEnrollFindSensorFragment() : Fragment() { factory = theFactory } - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } - private var animation: FingerprintFindSensorAnimation? = null - private val viewModel: FingerprintEnrollFindSensorViewModel by lazy { - viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java] + private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels { + factory ?: FingerprintEnrollFindSensorViewModel.Factory } override fun onCreateView( @@ -78,6 +74,12 @@ class RfpsEnrollFindSensorFragment() : Fragment() { // Set up footer bar val footerBarMixin = view.getMixin(FooterBarMixin::class.java) setupSecondaryButton(footerBarMixin) + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions()) + } + } lifecycleScope.launch { viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/SfpsEnrollFindSensorFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/SfpsEnrollFindSensorFragment.kt index 584824d4573..22705b5baea 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/SfpsEnrollFindSensorFragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/SfpsEnrollFindSensorFragment.kt @@ -24,11 +24,15 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView import com.android.settings.R import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterButton @@ -52,16 +56,8 @@ class SfpsEnrollFindSensorFragment() : Fragment() { factory = theFactory } - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } - - private val viewModel: FingerprintEnrollFindSensorViewModel by lazy { - viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java] + private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels { + factory ?: FingerprintEnrollFindSensorViewModel.Factory } override fun onCreateView( @@ -78,6 +74,12 @@ class SfpsEnrollFindSensorFragment() : Fragment() { val footerBarMixin = view.getMixin(FooterBarMixin::class.java) setupSecondaryButton(footerBarMixin) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions()) + } + } + // Set up lottie lifecycleScope.launch { viewModel.sfpsLottieInfo.collect { (isFolded, rotation) -> diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/UdfpsEnrollFindSensorFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/UdfpsEnrollFindSensorFragment.kt index 923a309cbac..17bfaa340b8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/UdfpsEnrollFindSensorFragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/education/UdfpsEnrollFindSensorFragment.kt @@ -24,11 +24,15 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.airbnb.lottie.LottieAnimationView import com.android.settings.R import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterButton @@ -53,16 +57,8 @@ class UdfpsEnrollFindSensorFragment() : Fragment() { factory = theFactory } - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } - - private val viewModel: FingerprintEnrollFindSensorViewModel by lazy { - viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java] + private val viewModel: FingerprintEnrollFindSensorViewModel by activityViewModels { + factory ?: FingerprintEnrollFindSensorViewModel.Factory } override fun onCreateView( @@ -79,6 +75,12 @@ class UdfpsEnrollFindSensorFragment() : Fragment() { val footerBarMixin = view.getMixin(FooterBarMixin::class.java) setupSecondaryButton(footerBarMixin) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions()) + } + } + lifecycleScope.launch { viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) } } 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 0ec0bdd3c26..1da0eeab133 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 @@ -28,12 +28,15 @@ import android.view.animation.Interpolator import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.android.settings.R import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel @@ -62,29 +65,21 @@ class RFPSEnrollFragment() : Fragment(R.layout.fingerprint_v2_rfps_enroll_enroll factory = theFactory } - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } - private lateinit var linearOutSlowInInterpolator: Interpolator private lateinit var fastOutLinearInInterpolator: Interpolator private lateinit var textView: TextView private lateinit var progressBar: RFPSProgressBar - private val iconTouchViewModel: RFPSIconTouchViewModel by lazy { - viewModelProvider[RFPSIconTouchViewModel::class.java] + private val iconTouchViewModel: RFPSIconTouchViewModel by viewModels { + RFPSIconTouchViewModel.Factory } - private val rfpsViewModel: RFPSViewModel by lazy { viewModelProvider[RFPSViewModel::class.java] } - - private val backgroundViewModel: BackgroundViewModel by lazy { - viewModelProvider[BackgroundViewModel::class.java] + private val rfpsViewModel: RFPSViewModel by activityViewModels { + factory ?: RFPSViewModel.Factory } + private val backgroundViewModel: BackgroundViewModel by activityViewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -131,6 +126,7 @@ class RFPSEnrollFragment() : Fragment(R.layout.fingerprint_v2_rfps_enroll_enroll viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { + rfpsViewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions()) // Icon animation update viewLifecycleOwner.lifecycleScope.launch { // TODO(b/324427704): Fix this delay diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSIconTouchViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSIconTouchViewModel.kt index cbcb1d4bbad..00af7ed56b8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSIconTouchViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/viewmodel/RFPSIconTouchViewModel.kt @@ -19,6 +19,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -27,6 +29,7 @@ import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.update private const val touchesToShowDialog = 3 + /** * This class is responsible for counting the number of touches on the fingerprint icon, and if this * number reaches a threshold it will produce an action via [shouldShowDialog] to indicate the ui @@ -52,10 +55,9 @@ class RFPSIconTouchViewModel : ViewModel() { _touches.update { _touches.value + 1 } } - class RFPSIconTouchViewModelFactory : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return RFPSIconTouchViewModel() as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { RFPSIconTouchViewModel() } } } } 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 8abcf1a3a93..c95020dff3c 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 @@ -16,9 +16,14 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel +import android.hardware.fingerprint.FingerprintEnrollOptions +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState @@ -158,27 +163,27 @@ class RFPSViewModel( enrollFlow = fingerprintEnrollViewModel.enrollFlow } - class RFPSViewModelFactory( - private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, - private val navigationViewModel: FingerprintNavigationViewModel, - private val orientationInteractor: OrientationInteractor, - private val fingerprintManager: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return RFPSViewModel( - fingerprintEnrollEnrollingViewModel, - navigationViewModel, - orientationInteractor, - fingerprintManager, - ) - as T - } + /** Starts enrollment. */ + fun enroll(enrollOptions: FingerprintEnrollOptions) { + fingerprintEnrollViewModel.enroll(enrollOptions) } companion object { private val navStep = Enrollment::class private const val TAG = "RFPSViewModel" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment!! + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + RFPSViewModel( + provider[FingerprintEnrollEnrollingViewModel::class], + provider[FingerprintNavigationViewModel::class], + biometricEnvironment.orientationInteractor, + biometricEnvironment.fingerprintManagerInteractor, + ) + } + } } } 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 dd7d9f53b3a..97900b741ea 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 @@ -82,6 +82,7 @@ class RFPSProgressBar : RingProgressBar { shouldAnimateInternal = shouldAnimate } + /** This function should only be called when actual progress has been made. */ fun updateProgress(percentComplete: Float) { val progress = maxProgress - (percentComplete.coerceIn(0.0f, 100.0f) * maxProgress).toInt() diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt index 4588a07db63..3015321e526 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt @@ -18,8 +18,6 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrol import android.os.Bundle import android.util.Log -import android.view.MotionEvent -import android.view.MotionEvent.ACTION_HOVER_MOVE import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -27,6 +25,7 @@ import android.widget.Button import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -36,6 +35,7 @@ import com.airbnb.lottie.LottieCompositionFactory import com.android.settings.R import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState +import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.widget.FingerprintErrorDialog import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText @@ -50,17 +50,10 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro /** Used for testing purposes */ private var factory: ViewModelProvider.Factory? = null - private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] } private lateinit var udfpsEnrollView: UdfpsEnrollViewV2 private lateinit var lottie: LottieAnimationView - private val viewModelProvider: ViewModelProvider by lazy { - if (factory != null) { - ViewModelProvider(requireActivity(), factory!!) - } else { - ViewModelProvider(requireActivity()) - } - } + private val viewModel: UdfpsViewModel by activityViewModels { factory ?: UdfpsViewModel.Factory } @VisibleForTesting constructor(theFactory: ViewModelProvider.Factory) : this() { @@ -90,6 +83,7 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.enroll(requireActivity().intent.toFingerprintEnrollOptions()) launch { viewModel.sensorLocation.collect { sensor -> udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType) @@ -204,12 +198,16 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro } viewLifecycleOwner.lifecycleScope.launch { - viewModel.touchExplorationDebug.collect { - udfpsEnrollView.sendDebugTouchExplorationEvent( - MotionEvent.obtain(100, 100, ACTION_HOVER_MOVE, it.x.toFloat(), it.y.toFloat(), 0) - ) + view.setOnTouchListener { _, motionEvent -> + viewModel.onTouchEvent(motionEvent) + false } } + + viewLifecycleOwner.lifecycleScope.launch { + viewModel.touchEvent.collect { udfpsEnrollView.onTouchEvent(it) } + } + viewModel.readyForEnrollment() } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt index 6a45ec4fcc2..8f9e45e838d 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsLastStepViewModel.kt @@ -16,9 +16,13 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState @@ -89,14 +93,18 @@ class UdfpsLastStepViewModel( } .filterNotNull() - class UdfpsLastStepViewModelFactory( - private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, - private val vibrationInteractor: VibrationInteractor, - ) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + UdfpsLastStepViewModel( + provider[FingerprintEnrollEnrollingViewModel::class], + biometricEnvironment!!.vibrationInteractor, + ) + } } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt index 508084e5916..3396cdc5243 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt @@ -16,21 +16,26 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel -import android.graphics.Point import android.graphics.PointF +import android.hardware.fingerprint.FingerprintEnrollOptions +import android.view.MotionEvent import android.view.Surface +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel -import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository -import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintSensorInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor +import com.android.settings.biometrics.fingerprint2.domain.interactor.TouchEventInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor @@ -45,6 +50,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged @@ -60,20 +66,20 @@ import kotlinx.coroutines.launch /** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */ class UdfpsViewModel( + val navigationViewModel: FingerprintNavigationViewModel, + val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, + backgroundViewModel: BackgroundViewModel, + val udfpsLastStepViewModel: UdfpsLastStepViewModel, val vibrationInteractor: VibrationInteractor, displayDensityInteractor: DisplayDensityInteractor, - val navigationViewModel: FingerprintNavigationViewModel, debuggingInteractor: DebuggingInteractor, - val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, - simulatedTouchEventsDebugRepository: SimulatedTouchEventsRepository, enrollStageInteractor: EnrollStageInteractor, orientationInteractor: OrientationInteractor, - backgroundViewModel: BackgroundViewModel, - sensorRepository: FingerprintSensorRepository, udfpsEnrollInteractor: UdfpsEnrollInteractor, fingerprintManager: FingerprintManagerInteractor, - val udfpsLastStepViewModel: UdfpsLastStepViewModel, accessibilityInteractor: AccessibilityInteractor, + sensorRepository: FingerprintSensorInteractor, + touchEventInteractor: TouchEventInteractor, ) : ViewModel() { private val isSetupWizard = flowOf(false) @@ -191,15 +197,9 @@ class UdfpsViewModel( .transform { emit(it == Surface.ROTATION_90) } .distinctUntilChanged() - /** This sends touch exploration events only used for debugging purposes. */ - val touchExplorationDebug: Flow = - debuggingInteractor.debuggingEnabled.combineTransform( - simulatedTouchEventsDebugRepository.touchExplorationDebug - ) { enabled, point -> - if (enabled) { - emit(point) - } - } + private val _touchEvent: MutableStateFlow = MutableStateFlow(null) + val touchEvent = + _touchEvent.asStateFlow().filterNotNull() /** Determines the current [EnrollStageModel] enrollment is in */ private val enrollStage: Flow = @@ -266,6 +266,12 @@ class UdfpsViewModel( viewModelScope.launch { backgroundViewModel.background.filter { it }.collect { didGoToBackground() } } + + viewModelScope.launch { + touchEventInteractor.touchEvent.collect { + _touchEvent.update { it } + } + } } /** Indicates if we should show the lottie. */ @@ -393,47 +399,43 @@ class UdfpsViewModel( ) } - class UdfpsEnrollmentFactory( - private val vibrationInteractor: VibrationInteractor, - private val displayDensityInteractor: DisplayDensityInteractor, - private val navigationViewModel: FingerprintNavigationViewModel, - private val debuggingInteractor: DebuggingInteractor, - private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, - private val simulatedTouchEventsRepository: SimulatedTouchEventsRepository, - private val enrollStageInteractor: EnrollStageInteractor, - private val orientationInteractor: OrientationInteractor, - private val backgroundViewModel: BackgroundViewModel, - private val sensorRepository: FingerprintSensorRepository, - private val udfpsEnrollInteractor: UdfpsEnrollInteractor, - private val fingerprintManager: FingerprintManagerInteractor, - private val udfpsLastStepViewModel: UdfpsLastStepViewModel, - private val accessibilityInteractor: AccessibilityInteractor, - ) : ViewModelProvider.Factory { + /** Starts enrollment. */ + fun enroll(enrollOptions: FingerprintEnrollOptions) { + fingerprintEnrollEnrollingViewModel.enroll(enrollOptions) + } - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return UdfpsViewModel( - vibrationInteractor, - displayDensityInteractor, - navigationViewModel, - debuggingInteractor, - fingerprintEnrollEnrollingViewModel, - simulatedTouchEventsRepository, - enrollStageInteractor, - orientationInteractor, - backgroundViewModel, - sensorRepository, - udfpsEnrollInteractor, - fingerprintManager, - udfpsLastStepViewModel, - accessibilityInteractor, - ) - as T - } + /** Indicates a touch event has occurred. */ + fun onTouchEvent(event: MotionEvent) { + _touchEvent.update { event } } companion object { private val navStep = FingerprintNavigationStep.Enrollment::class private const val TAG = "UDFPSViewModel" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment!! + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + + UdfpsViewModel( + provider[FingerprintNavigationViewModel::class], + provider[FingerprintEnrollEnrollingViewModel::class], + provider[BackgroundViewModel::class], + provider[UdfpsLastStepViewModel::class], + biometricEnvironment.vibrationInteractor, + biometricEnvironment.displayDensityInteractor, + biometricEnvironment.debuggingInteractor, + biometricEnvironment.enrollStageInteractor, + biometricEnvironment.orientationInteractor, + biometricEnvironment.udfpsEnrollInteractor, + biometricEnvironment.fingerprintManagerInteractor, + biometricEnvironment.accessibilityInteractor, + biometricEnvironment.sensorInteractor, + biometricEnvironment.touchEventInteractor, + ) + } + } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt index c3adc87fc7d..05d25cd5fe2 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt @@ -210,6 +210,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr override fun getOpacity(): Int { return PixelFormat.UNKNOWN } + /** * Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with * respect to the parent framelayout. diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt index d9593c117ad..286d7501c46 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt @@ -148,18 +148,10 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co } } - /** - * Sends a touch exploration event to the [onHoverListener] this should only be used for - * debugging. - */ - fun sendDebugTouchExplorationEvent(motionEvent: MotionEvent) { - touchExplorationAnnouncer.onTouch(motionEvent) - } - /** Sets the addHoverListener, this should happen when talkback is enabled. */ private fun addHoverListener() { onHoverListener = OnHoverListener { _: View, event: MotionEvent -> - sendDebugTouchExplorationEvent(event) + touchExplorationAnnouncer.onTouch(event) false } this.setOnHoverListener(onHoverListener) diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/BackgroundViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/BackgroundViewModel.kt index 2b53a530f4b..75a22fcb8a8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/BackgroundViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/BackgroundViewModel.kt @@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update @@ -39,10 +41,9 @@ class BackgroundViewModel : ViewModel() { _background.update { false } } - class BackgroundViewModelFactory : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return BackgroundViewModel() as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { BackgroundViewModel() } } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt index d9b31d7f622..5ce2ed756d8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt @@ -16,14 +16,17 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel +import android.util.Log +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import kotlinx.coroutines.flow.Flow -/** - * Models the UI state for [FingerprintEnrollConfirmationV2Fragment] - */ +/** Models the UI state for [FingerprintEnrollConfirmationV2Fragment] */ class FingerprintEnrollConfirmationViewModel( private val navigationViewModel: FingerprintNavigationViewModel, fingerprintInteractor: FingerprintManagerInteractor, @@ -50,18 +53,20 @@ class FingerprintEnrollConfirmationViewModel( navigationViewModel.update(FingerprintAction.ADD_ANOTHER, navStep, "onAddAnotherButtonClicked") } - class FingerprintEnrollConfirmationViewModelFactory( - private val navigationViewModel: FingerprintNavigationViewModel, - private val fingerprintInteractor: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintInteractor) as T - } - } - companion object { private const val TAG = "FingerprintEnrollConfirmationViewModel" private val navStep = FingerprintNavigationStep.Confirmation::class + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + FingerprintEnrollConfirmationViewModel( + provider[FingerprintNavigationViewModel::class], + biometricEnvironment!!.fingerprintManagerInteractor, + ) + } + } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt index bbe3cfd2775..a2accb7e04b 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModel.kt @@ -16,14 +16,15 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel -import android.util.Log +import android.hardware.fingerprint.FingerprintEnrollOptions +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.android.systemui.biometrics.shared.model.FingerprintSensor +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.update @@ -69,23 +70,24 @@ class FingerprintEnrollEnrollingViewModel( val enrollFlow = enrollFlowShouldBeRunning.transformLatest { if (it) { - fingerprintEnrollViewModel.enrollFlow.collect { event -> - emit(event) } + fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) } } } - class FingerprintEnrollEnrollingViewModelFactory( - private val fingerprintEnrollViewModel: FingerprintEnrollViewModel, - private val backgroundViewModel: BackgroundViewModel, - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel) - as T - } + /** Indicates enrollment to start */ + fun enroll(enrollOptions: FingerprintEnrollOptions) { + fingerprintEnrollViewModel.enroll(enrollOptions) } companion object { - private val TAG = "FingerprintEnrollEnrollingViewModel" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + FingerprintEnrollEnrollingViewModel( + provider[FingerprintEnrollViewModel::class], + provider[BackgroundViewModel::class], + ) + } + } } } 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 ddbf1cbd777..3568dbdd511 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 @@ -16,9 +16,14 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel +import android.hardware.fingerprint.FingerprintEnrollOptions +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor @@ -43,10 +48,10 @@ class FingerprintEnrollFindSensorViewModel( private val fingerprintEnrollViewModel: FingerprintEnrollViewModel, private val gatekeeperViewModel: FingerprintGatekeeperViewModel, backgroundViewModel: BackgroundViewModel, + fingerprintFlowViewModel: FingerprintFlowViewModel, accessibilityInteractor: AccessibilityInteractor, foldStateInteractor: FoldStateInteractor, orientationInteractor: OrientationInteractor, - fingerprintFlowViewModel: FingerprintFlowViewModel, fingerprintManagerInteractor: FingerprintManagerInteractor, ) : ViewModel() { @@ -64,6 +69,7 @@ class FingerprintEnrollFindSensorViewModel( val showPrimaryButton: Flow = _isUdfps.filter { it } private val _showSfpsLottie = _isSfps.filter { it } + /** Represents the stream of showing sfps lottie and the information Pair(isFolded, rotation). */ val sfpsLottieInfo: Flow> = combineTransform( @@ -75,6 +81,7 @@ class FingerprintEnrollFindSensorViewModel( } private val _showUdfpsLottie = _isUdfps.filter { it } + /** Represents the stream of showing udfps lottie and whether accessibility is enabled. */ val udfpsLottieInfo: Flow = _showUdfpsLottie.combine(accessibilityInteractor.isAccessibilityEnabled) { @@ -87,11 +94,13 @@ class FingerprintEnrollFindSensorViewModel( val showRfpsAnimation: Flow = _isRearSfps.filter { it } private val _showErrorDialog: MutableStateFlow?> = MutableStateFlow(null) + /** Represents the stream of showing error dialog. */ val showErrorDialog = _showErrorDialog.filterNotNull() private var _didTryEducation = false private var _education: MutableStateFlow = MutableStateFlow(false) + /** Indicates if the education flow should be running. */ private val educationFlowShouldBeRunning: Flow = _education.combine(backgroundViewModel.background) { shouldRunEducation, isInBackground -> @@ -167,6 +176,11 @@ class FingerprintEnrollFindSensorViewModel( _education.update { false } } + /** Indicates enrollment to start */ + fun enroll(enrollOptions: FingerprintEnrollOptions) { + fingerprintEnrollViewModel.enroll(enrollOptions) + } + /** Proceed to EnrollEnrolling page. */ fun proceedToEnrolling() { stopEducation() @@ -182,36 +196,29 @@ class FingerprintEnrollFindSensorViewModel( ) } - class FingerprintEnrollFindSensorViewModelFactory( - private val navigationViewModel: FingerprintNavigationViewModel, - private val fingerprintEnrollViewModel: FingerprintEnrollViewModel, - private val gatekeeperViewModel: FingerprintGatekeeperViewModel, - private val backgroundViewModel: BackgroundViewModel, - private val accessibilityInteractor: AccessibilityInteractor, - private val foldStateInteractor: FoldStateInteractor, - private val orientationInteractor: OrientationInteractor, - private val fingerprintFlowViewModel: FingerprintFlowViewModel, - private val fingerprintManagerInteractor: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintEnrollFindSensorViewModel( - navigationViewModel, - fingerprintEnrollViewModel, - gatekeeperViewModel, - backgroundViewModel, - accessibilityInteractor, - foldStateInteractor, - orientationInteractor, - fingerprintFlowViewModel, - fingerprintManagerInteractor, - ) - as T - } - } - companion object { private const val TAG = "FingerprintEnrollFindSensorViewModel" private val navStep = Education::class + + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment!! + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + + FingerprintEnrollFindSensorViewModel( + provider[FingerprintNavigationViewModel::class], + provider[FingerprintEnrollViewModel::class], + provider[FingerprintGatekeeperViewModel::class], + provider[BackgroundViewModel::class], + provider[FingerprintFlowViewModel::class], + biometricEnvironment.accessibilityInteractor, + biometricEnvironment.foldStateInteractor, + biometricEnvironment.orientationInteractor, + biometricEnvironment.fingerprintManagerInteractor, + ) + } + } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt index 3bf003c0c90..6ec204815e1 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollIntroViewModel.kt @@ -16,8 +16,12 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Introduction @@ -27,8 +31,8 @@ import kotlinx.coroutines.flow.Flow /** A view model for fingerprint enroll introduction. */ class FingerprintEnrollIntroViewModel( val navigationViewModel: FingerprintNavigationViewModel, - private val fingerprintFlowViewModel: FingerprintFlowViewModel, - private val fingerprintManagerInteractor: FingerprintManagerInteractor, + fingerprintFlowViewModel: FingerprintFlowViewModel, + fingerprintManagerInteractor: FingerprintManagerInteractor, ) : ViewModel() { /** Represents a stream of [FingerprintSensor] */ @@ -51,25 +55,21 @@ class FingerprintEnrollIntroViewModel( ) } - class FingerprintEnrollIntoViewModelFactory( - val navigationViewModel: FingerprintNavigationViewModel, - val fingerprintFlowViewModel: FingerprintFlowViewModel, - val fingerprintManagerInteractor: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintEnrollIntroViewModel( - navigationViewModel, - fingerprintFlowViewModel, - fingerprintManagerInteractor, - ) - as T - } - } - companion object { val navStep = Introduction::class private const val TAG = "FingerprintEnrollIntroViewModel" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + FingerprintEnrollIntroViewModel( + provider[FingerprintNavigationViewModel::class], + provider[FingerprintFlowViewModel::class], + biometricEnvironment!!.fingerprintManagerInteractor, + ) + } + } } } 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 c27808db434..2669b8bbe5b 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 @@ -15,9 +15,15 @@ */ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel +import android.hardware.fingerprint.FingerprintEnrollOptions +import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState @@ -25,12 +31,14 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment import com.android.systemui.biometrics.shared.model.FingerprintSensorType import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.transformLatest +import kotlinx.coroutines.flow.update /** Represents all of the fingerprint information needed for a fingerprint enrollment process. */ class FingerprintEnrollViewModel( @@ -55,6 +63,8 @@ class FingerprintEnrollViewModel( } } + private val enrollOptions: MutableStateFlow = MutableStateFlow(null) + /** Represents the stream of [FingerprintSensorType] */ val sensorType: Flow = fingerprintManagerInteractor.sensorPropertiesInternal.filterNotNull().map { it.sensorType } @@ -66,16 +76,23 @@ class FingerprintEnrollViewModel( * This flow should be the only flow which calls enroll(). */ val _enrollFlow: Flow = - combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason) { hardwareAuthToken, enrollReason -> - Pair(hardwareAuthToken, enrollReason) + combine(gatekeeperViewModel.gatekeeperInfo, _enrollReason, enrollOptions) { + hardwareAuthToken, + enrollReason, + enrollOptions -> + Triple(hardwareAuthToken, enrollReason, enrollOptions) } .transformLatest { /** [transformLatest] is used as we want to make sure to cancel previous API call. */ - (hardwareAuthToken, enrollReason) -> - if (hardwareAuthToken is GatekeeperInfo.GatekeeperPasswordInfo && enrollReason != null) { - fingerprintManagerInteractor.enroll(hardwareAuthToken.token, enrollReason).collect { - emit(it) - } + (hardwareAuthToken, enrollReason, enrollOptions) -> + if ( + hardwareAuthToken is GatekeeperInfo.GatekeeperPasswordInfo && + enrollReason != null && + enrollOptions != null + ) { + fingerprintManagerInteractor + .enroll(hardwareAuthToken.token, enrollReason, enrollOptions) + .collect { emit(it) } } } .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) @@ -108,14 +125,23 @@ class FingerprintEnrollViewModel( } } - class FingerprintEnrollViewModelFactory( - val interactor: FingerprintManagerInteractor, - val gatekeeperViewModel: FingerprintGatekeeperViewModel, - val navigationViewModel: FingerprintNavigationViewModel, - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) as T + /** Starts enrollment. */ + fun enroll(options: FingerprintEnrollOptions) { + enrollOptions.update { options } + } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = this[APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + val provider = ViewModelProvider(this[VIEW_MODEL_STORE_OWNER_KEY]!!) + FingerprintEnrollViewModel( + biometricEnvironment!!.fingerprintManagerInteractor, + provider[FingerprintGatekeeperViewModel::class], + provider[FingerprintNavigationViewModel::class], + ) + } } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintFlowViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintFlowViewModel.kt index f07652419d0..394c2725281 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintFlowViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintFlowViewModel.kt @@ -19,21 +19,28 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.update -class FingerprintFlowViewModel(private val fingerprintFlowType: FingerprintFlow) : ViewModel() { - val fingerprintFlow: Flow = - flowOf(fingerprintFlowType).shareIn(viewModelScope, SharingStarted.Eagerly, 1) +class FingerprintFlowViewModel() : ViewModel() { + val _mutableFingerprintFlow: MutableStateFlow = MutableStateFlow(null) + val fingerprintFlow: Flow = + _mutableFingerprintFlow.shareIn(viewModelScope, SharingStarted.Eagerly, 1) - class FingerprintFlowViewModelFactory(val flowType: FingerprintFlow) : ViewModelProvider.Factory { + /** Used to set the fingerprint flow type */ + fun updateFlowType(fingerprintFlowType: FingerprintFlow) { + _mutableFingerprintFlow.update { fingerprintFlowType } + } - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintFlowViewModel(flowType) as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { FingerprintFlowViewModel() } } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt index 322be6af100..b5be165056e 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt @@ -21,6 +21,9 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -47,12 +50,10 @@ sealed interface GatekeeperInfo { * in as a parameter to this class. */ class FingerprintGatekeeperViewModel( - theGatekeeperInfo: GatekeeperInfo?, - private val fingerprintManagerInteractor: FingerprintManagerInteractor, + private val fingerprintManagerInteractor: FingerprintManagerInteractor ) : ViewModel() { - private var _gatekeeperInfo: MutableStateFlow = - MutableStateFlow(theGatekeeperInfo) + private var _gatekeeperInfo: MutableStateFlow = MutableStateFlow(null) /** The gatekeeper info for fingerprint enrollment. */ val gatekeeperInfo: Flow = _gatekeeperInfo.asStateFlow() @@ -61,26 +62,27 @@ class FingerprintGatekeeperViewModel( val hasValidGatekeeperInfo: Flow = gatekeeperInfo.map { it is GatekeeperInfo.GatekeeperPasswordInfo } - private var _credentialConfirmed: MutableStateFlow = MutableStateFlow(null) - val credentialConfirmed: Flow = _credentialConfirmed.asStateFlow() - private var countDownTimer: CountDownTimer? = null /** Timeout of 15 minutes for a generated challenge */ private val TIMEOUT: Long = 15 * 60 * 1000 /** Called after a confirm device credential attempt has been made. */ - fun onConfirmDevice(wasSuccessful: Boolean, theGatekeeperPasswordHandle: Long?) { + fun onConfirmDevice( + wasSuccessful: Boolean, + theGatekeeperPasswordHandle: Long?, + shouldStartTimer: Boolean = true, + ) { if (!wasSuccessful) { Log.d(TAG, "confirmDevice failed") _gatekeeperInfo.update { GatekeeperInfo.Invalid } - _credentialConfirmed.update { false } } else { viewModelScope.launch { val res = fingerprintManagerInteractor.generateChallenge(theGatekeeperPasswordHandle!!) _gatekeeperInfo.update { GatekeeperInfo.GatekeeperPasswordInfo(res.second, res.first) } - _credentialConfirmed.update { true } - startTimeout() + if (shouldStartTimer) { + startTimeout() + } } } } @@ -97,19 +99,9 @@ class FingerprintGatekeeperViewModel( } } - class FingerprintGatekeeperViewModelFactory( - private val gatekeeperInfo: GatekeeperInfo?, - private val fingerprintManagerInteractor: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintGatekeeperViewModel(gatekeeperInfo, fingerprintManagerInteractor) as T - } - } - companion object { private const val TAG = "FingerprintGatekeeperViewModel" + /** * A function that checks if the challenge and token are valid, in which case a * [GatekeeperInfo.GatekeeperPasswordInfo] is provided, else [GatekeeperInfo.Invalid] @@ -121,5 +113,14 @@ class FingerprintGatekeeperViewModel( } return GatekeeperInfo.GatekeeperPasswordInfo(token, challenge) } + + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + FingerprintGatekeeperViewModel(biometricEnvironment!!.fingerprintManagerInteractor) + } + } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt index 131f5bb1e96..caf7d2a8a9b 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationViewModel.kt @@ -20,7 +20,11 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.android.settings.SettingsApplication import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor +import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Finish import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.TransitionStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.UiStep @@ -28,39 +32,40 @@ import java.lang.NullPointerException import kotlin.reflect.KClass import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch /** * This class is essentially a wrapper around [FingerprintNavigationStep] that will be used by * fragments/viewmodels that want to consume these events. It should provide no additional * functionality beyond what is available in [FingerprintNavigationStep]. */ -class FingerprintNavigationViewModel( - step: UiStep, - hasConfirmedDeviceCredential: Boolean, - flowViewModel: FingerprintFlowViewModel, - fingerprintManagerInteractor: FingerprintManagerInteractor, -) : ViewModel() { +class FingerprintNavigationViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) : + ViewModel() { - private var _navStateInternal: MutableStateFlow = MutableStateFlow(null) - - init { - viewModelScope.launch { - flowViewModel.fingerprintFlow - .combineTransform(fingerprintManagerInteractor.sensorPropertiesInternal) { flow, props -> - if (props?.sensorId != -1) { - emit(NavigationState(flow, hasConfirmedDeviceCredential, props)) - } + private val _flowInternal: MutableStateFlow = MutableStateFlow(null) + private val _hasConfirmedDeviceCredential: MutableStateFlow = MutableStateFlow(false) + private val _navStateInternal: StateFlow = + combine( + _flowInternal, + _hasConfirmedDeviceCredential, + fingerprintManagerInteractor.sensorPropertiesInternal, + ) { flow, hasConfirmed, sensorType -> + if (flow == null || sensorType == null) { + return@combine null } - .collect { navState -> _navStateInternal.update { navState } } - } - } + return@combine NavigationState(flow, hasConfirmed, sensorType) + } + .stateIn(viewModelScope, SharingStarted.Eagerly, null) - private var _currentStep = MutableStateFlow(step) + private var _currentStep = + MutableStateFlow(FingerprintNavigationStep.Init) private var _navigateTo: MutableStateFlow = MutableStateFlow(null) val navigateTo: Flow = _navigateTo.asStateFlow() @@ -85,6 +90,16 @@ class FingerprintNavigationViewModel( /** This indicates what screen should currently be presenting to the user. */ val currentScreen: Flow = _currentScreen.asStateFlow() + /** Updates the type of flow the navigation should begin */ + fun updateFingerprintFlow(flow: FingerprintFlow) { + _flowInternal.update { flow } + } + + /** Indicates if we have confirmed device credential */ + fun hasConfirmedDeviceCredential(hasConfirmedDeviceCredential: Boolean) { + _hasConfirmedDeviceCredential.update { hasConfirmedDeviceCredential } + } + /** See [updateInternal] for more details */ fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) { Log.d(TAG, "$caller.update($action) $debugStr") @@ -122,26 +137,15 @@ class FingerprintNavigationViewModel( } } - class FingerprintNavigationViewModelFactory( - private val step: UiStep, - private val hasConfirmedDeviceCredential: Boolean, - private val flowViewModel: FingerprintFlowViewModel, - private val fingerprintManagerInteractor: FingerprintManagerInteractor, - ) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintNavigationViewModel( - step, - hasConfirmedDeviceCredential, - flowViewModel, - fingerprintManagerInteractor, - ) - as T - } - } - companion object { private const val TAG = "FingerprintNavigationViewModel" + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val settingsApplication = + this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as SettingsApplication + val biometricEnvironment = settingsApplication.biometricEnvironment + FingerprintNavigationViewModel(biometricEnvironment!!.fingerprintManagerInteractor) + } + } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintScrollViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintScrollViewModel.kt index bc9703d2ad2..ddef0dfd36b 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintScrollViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintScrollViewModel.kt @@ -18,6 +18,8 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -36,11 +38,9 @@ class FingerprintScrollViewModel : ViewModel() { _hasReadConsentScreen.update { true } } - class FingerprintScrollViewModelFactory : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return FingerprintScrollViewModel() as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { FingerprintScrollViewModel() } } } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt index 8fb72916cd7..d1a93260b23 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/TransitionViewModel.kt @@ -18,24 +18,15 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel /** Indicates the type of transitions that can occur between fragments */ sealed class Transition { - /** - * Indicates the new fragment should slide in from the left side - */ + /** Indicates the new fragment should slide in from the left side */ data object EnterFromLeft : Transition() - /** - * Indicates the new fragment should slide in from the right side - */ + /** Indicates the new fragment should slide in from the right side */ data object EnterFromRight : Transition() - /** - * Indicates the old fragment should slide out to the left side - */ + /** Indicates the old fragment should slide out to the left side */ data object ExitToLeft : Transition() - /** - * Indicates the old fragment should slide out to the right side - */ + /** Indicates the old fragment should slide out to the right side */ data object ExitToRight : Transition() } - diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/binder/FingerprintSettingsViewBinder.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/binder/FingerprintSettingsViewBinder.kt index 540e5ee9f52..c71062f67e8 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/binder/FingerprintSettingsViewBinder.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/binder/FingerprintSettingsViewBinder.kt @@ -54,29 +54,40 @@ object FingerprintSettingsViewBinder { challenge: Long?, challengeToken: ByteArray?, ) + /** Helper to launch an add fingerprint request */ fun launchAddFingerprint(userId: Int, challengeToken: ByteArray?) + /** * Helper function that will try and launch confirm lock, if that fails we will prompt user to * choose a PIN/PATTERN/PASS. */ fun launchConfirmOrChooseLock(userId: Int) + /** Used to indicate that FingerprintSettings is finished. */ fun finish() + /** Indicates what result should be set for the returning callee */ fun setResultExternal(resultCode: Int) + /** Indicates the settings UI should be shown */ fun showSettings(enrolledFingerprints: List) + /** Updates the add fingerprints preference */ fun updateAddFingerprintsPreference(canEnroll: Boolean, maxFingerprints: Int) + /** Updates the sfps fingerprints preference */ fun updateSfpsPreference(isSfpsPrefVisible: Boolean) + /** Indicates that a user has been locked out */ fun userLockout(authAttemptViewModel: FingerprintAuthAttemptModel.Error) + /** Indicates a fingerprint preference should be highlighted */ suspend fun highlightPref(fingerId: Int) + /** Indicates a user should be prompted to delete a fingerprint */ suspend fun askUserToDeleteDialog(fingerprintViewModel: FingerprintData): Boolean + /** Indicates a user should be asked to renae ma dialog */ suspend fun askUserToRenameDialog( fingerprintViewModel: FingerprintData 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 7900ed725bc..4c3773bb3e9 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,14 +45,13 @@ import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED import com.android.settings.biometrics.GatekeeperPasswordProvider import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroductionInternal -import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintEnrollInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.Settings -import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.common.util.toFingerprintEnrollOptions import com.android.settings.biometrics.fingerprint2.ui.settings.binder.FingerprintSettingsViewBinder import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsNavigationViewModel import com.android.settings.biometrics.fingerprint2.ui.settings.viewmodel.FingerprintSettingsViewModel @@ -227,7 +226,6 @@ class FingerprintSettingsV2Fragment : val fingerprintEnrollStateRepository = FingerprintEnrollInteractorImpl( requireContext().applicationContext, - intent.toFingerprintEnrollOptions(), fingerprintManager, Settings, ) diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt index 00b91a8c92e..8a694aeac91 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsNavigationViewModel.kt @@ -52,7 +52,7 @@ class FingerprintSettingsNavigationViewModel( _nextStep.update { LaunchConfirmDeviceCredential(userId) } } else { viewModelScope.launch { - if (fingerprintManagerInteractor.enrolledFingerprints.last().isEmpty()) { + if (fingerprintManagerInteractor.enrolledFingerprints.last()?.isEmpty() == true) { _nextStep.update { EnrollFirstFingerprint(userId, null, challenge, token) } } else { showSettingsHelper() @@ -149,7 +149,7 @@ class FingerprintSettingsNavigationViewModel( private suspend fun launchEnrollNextStep(gateKeeperPasswordHandle: Long?) { fingerprintManagerInteractor.enrolledFingerprints.collect { - if (it.isEmpty()) { + if (it?.isEmpty() == true) { _nextStep.update { EnrollFirstFingerprint(userId, gateKeeperPasswordHandle, null, null) } } else { viewModelScope.launch(backgroundDispatcher) { diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt index 80bbb43efe7..cf8c527d6d4 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/viewmodel/FingerprintSettingsViewModel.kt @@ -74,7 +74,7 @@ class FingerprintSettingsViewModel( /** Represents the stream of visibility of sfps preference. */ val isSfpsPrefVisible: Flow = _enrolledFingerprints.filterOnlyWhenSettingsIsShown().transform { - emit(fingerprintManagerInteractor.hasSideFps() && !it.isNullOrEmpty()) + emit(fingerprintManagerInteractor.hasSideFps() == true && !it.isNullOrEmpty()) } private val _isShowingDialog: MutableStateFlow = MutableStateFlow(null) diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt index e30819bcb2f..92868809494 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt @@ -67,7 +67,6 @@ class FingerprintEnrollIntroFragmentTest { private val gatekeeperViewModel = FingerprintGatekeeperViewModel( - GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L), interactor ) private val backgroundDispatcher = StandardTestDispatcher() @@ -86,13 +85,10 @@ class FingerprintEnrollIntroFragmentTest { .toFingerprintSensor() var enrollFlow = Default - val flowViewModel = FingerprintFlowViewModel(enrollFlow) + val flowViewModel = FingerprintFlowViewModel() private val navigationViewModel = FingerprintNavigationViewModel( - Introduction(), - false, - flowViewModel, interactor ) @@ -124,6 +120,11 @@ class FingerprintEnrollIntroFragmentTest { } } + gatekeeperViewModel.onConfirmDevice(true, 100L, false) + flowViewModel.updateFlowType(enrollFlow) + navigationViewModel.hasConfirmedDeviceCredential(true) + navigationViewModel.updateFingerprintFlow(enrollFlow) + fragmentScenario = launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) { FingerprintEnrollIntroV2Fragment(factory) diff --git a/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png b/tests/screenshot/assets/robolectric/rfps_enroll_confirmation.png similarity index 100% rename from tests/screenshot/assets/robolectric/fp_enroll_confirmation.png rename to tests/screenshot/assets/robolectric/rfps_enroll_confirmation.png diff --git a/tests/screenshot/assets/robolectric/fp_enroll_enrolling.png b/tests/screenshot/assets/robolectric/rfps_enroll_enrolling.png similarity index 100% rename from tests/screenshot/assets/robolectric/fp_enroll_enrolling.png rename to tests/screenshot/assets/robolectric/rfps_enroll_enrolling.png diff --git a/tests/screenshot/assets/robolectric/fp_enroll_find_sensor.png b/tests/screenshot/assets/robolectric/rfps_enroll_find_sensor.png similarity index 100% rename from tests/screenshot/assets/robolectric/fp_enroll_find_sensor.png rename to tests/screenshot/assets/robolectric/rfps_enroll_find_sensor.png diff --git a/tests/screenshot/assets/robolectric/fp_enroll_intro.png b/tests/screenshot/assets/robolectric/rfps_enroll_intro.png similarity index 100% rename from tests/screenshot/assets/robolectric/fp_enroll_intro.png rename to tests/screenshot/assets/robolectric/rfps_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 index 652afa06143..19433f3cb19 100644 --- 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 @@ -42,7 +42,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo import com.android.settings.testutils2.FakeFingerprintManagerInteractor import com.android.systemui.biometrics.shared.model.toFingerprintSensor import kotlinx.coroutines.flow.Flow @@ -95,23 +94,27 @@ class Injector(step: FingerprintNavigationStep.UiStep) { override fun getRotationFromDefault(rotation: Int): Int = rotation } - var gatekeeperViewModel = - FingerprintGatekeeperViewModel( - GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L), - interactor, - ) + var gatekeeperViewModel = FingerprintGatekeeperViewModel(fingerprintManagerInteractor) - val flowViewModel = FingerprintFlowViewModel(enrollFlow) + val flowViewModel = FingerprintFlowViewModel() - var navigationViewModel = FingerprintNavigationViewModel(step, true, flowViewModel, interactor) + var navigationViewModel = FingerprintNavigationViewModel(fingerprintManagerInteractor) var fingerprintViewModel = - FingerprintEnrollIntroViewModel(navigationViewModel, flowViewModel, interactor) + FingerprintEnrollIntroViewModel( + navigationViewModel, + flowViewModel, + fingerprintManagerInteractor, + ) var fingerprintScrollViewModel = FingerprintScrollViewModel() var backgroundViewModel = BackgroundViewModel() var fingerprintEnrollViewModel = - FingerprintEnrollViewModel(interactor, gatekeeperViewModel, navigationViewModel) + FingerprintEnrollViewModel( + fingerprintManagerInteractor, + gatekeeperViewModel, + navigationViewModel, + ) var fingerprintEnrollEnrollingViewModel = FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel) @@ -122,11 +125,11 @@ class Injector(step: FingerprintNavigationStep.UiStep) { fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor, - interactor, + fingerprintManagerInteractor, ) val fingerprintEnrollConfirmationViewModel = - FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor) + FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintManagerInteractor) var fingerprintFindSensorViewModel = FingerprintEnrollFindSensorViewModel( @@ -134,11 +137,11 @@ class Injector(step: FingerprintNavigationStep.UiStep) { fingerprintEnrollViewModel, gatekeeperViewModel, backgroundViewModel, + flowViewModel, accessibilityInteractor, foldStateInteractor, orientationInteractor, - flowViewModel, - interactor, + fingerprintManagerInteractor, ) val factory = @@ -166,12 +169,16 @@ class Injector(step: FingerprintNavigationStep.UiStep) { init { fingerprintEnrollViewModel.sensorTypeCached = fingerprintSensor.sensorType + gatekeeperViewModel.onConfirmDevice(true, 100L) + navigationViewModel.updateFingerprintFlow(enrollFlow) + navigationViewModel.hasConfirmedDeviceCredential(true) + flowViewModel.updateFlowType(enrollFlow) } companion object { private val Phone = DisplaySpec("phone", width = 1080, height = 2340, densityDpi = 420) private const val screenshotPath = "/settings_screenshots" - val interactor = FakeFingerprintManagerInteractor() + val fingerprintManagerInteractor = FakeFingerprintManagerInteractor() fun BiometricFragmentScreenShotRule() = FragmentScreenshotTestRule( diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollConfirmationScreenshotTest.kt similarity index 86% rename from tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt rename to tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollConfirmationScreenshotTest.kt index 28f4fbec5d5..6c49a0c4efd 100644 --- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollConfirmationScreenshotTest.kt @@ -17,7 +17,6 @@ package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.android.settings.tests.screenshot.biometrics.fingerprint.Injector @@ -28,17 +27,15 @@ import platform.test.screenshot.FragmentScreenshotTestRule import platform.test.screenshot.ViewScreenshotTestRule.Mode @RunWith(AndroidJUnit4::class) -class FingerprintEnrollConfirmationScreenshotTest { +class RfpsEnrollConfirmationScreenshotTest { private val injector: Injector = Injector(FingerprintNavigationStep.Confirmation) - @Rule - @JvmField - var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule() + @Rule @JvmField var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule() @Test fun testConfirmation() { rule.screenshotTest( - "fp_enroll_confirmation", + "rfps_enroll_confirmation", Mode.MatchSize, FingerprintEnrollConfirmationV2Fragment(injector.factory), ) 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/RfpsEnrollEnrollingScreenshotTest.kt similarity index 86% rename from tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollEnrollingScreenshotTest.kt rename to tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollEnrollingScreenshotTest.kt index 215e76fc8c1..48ff59b717f 100644 --- 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/RfpsEnrollEnrollingScreenshotTest.kt @@ -27,14 +27,14 @@ import platform.test.screenshot.FragmentScreenshotTestRule import platform.test.screenshot.ViewScreenshotTestRule.Mode @RunWith(AndroidJUnit4::class) -class FingerprintEnrollEnrollingScreenshotTest { +class RfpsEnrollEnrollingScreenshotTest { private val injector: Injector = - Injector(FingerprintNavigationStep.Enrollment(Injector.interactor.sensorProp)) + Injector(FingerprintNavigationStep.Enrollment(Injector.fingerprintManagerInteractor.sensorProp)) @Rule @JvmField var rule: FragmentScreenshotTestRule = BiometricFragmentScreenShotRule() @Test fun testEnrollEnrolling() { - rule.screenshotTest("fp_enroll_enrolling", Mode.MatchSize, RFPSEnrollFragment(injector.factory)) + rule.screenshotTest("rfps_enroll_enrolling", Mode.MatchSize, RFPSEnrollFragment(injector.factory)) } } diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollFindSensorScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollFindSensorScreenshotTest.kt index 594aade20bb..01cc0e649ae 100644 --- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollFindSensorScreenshotTest.kt +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollFindSensorScreenshotTest.kt @@ -31,10 +31,6 @@ class RfpsEnrollFindSensorScreenshotTest { @Test fun testEnrollFindSensor() { - rule.screenshotTest( - "fp_enroll_find_sensor", - Mode.MatchSize, - RfpsEnrollFindSensorFragment(), - ) + rule.screenshotTest("rfps_enroll_find_sensor", Mode.MatchSize, RfpsEnrollFindSensorFragment()) } } 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/RfpsEnrollIntroScreenshotTest.kt similarity index 89% rename from tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollIntroScreenshotTest.kt rename to tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/RfpsEnrollIntroScreenshotTest.kt index 68d600b1298..128d54017ce 100644 --- 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/RfpsEnrollIntroScreenshotTest.kt @@ -27,17 +27,15 @@ import platform.test.screenshot.FragmentScreenshotTestRule import platform.test.screenshot.ViewScreenshotTestRule.Mode @RunWith(AndroidJUnit4::class) -class FingerprintEnrollIntroScreenshotTest { +class RfpsEnrollIntroScreenshotTest { private val injector: Injector = Injector(FingerprintNavigationStep.Introduction()) - @Rule - @JvmField - var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule() + @Rule @JvmField var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule() @Test fun testEnrollIntro() { rule.screenshotTest( - "fp_enroll_intro", + "rfps_enroll_intro", Mode.MatchSize, FingerprintEnrollIntroV2Fragment(injector.factory), ) diff --git a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt index b39f0d18522..52df724b82d 100644 --- a/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt +++ b/tests/shared/src/com/android/settings/testutils2/FakeFingerprintManagerInteractor.kt @@ -19,6 +19,7 @@ package com.android.settings.testutils2 import android.hardware.biometrics.ComponentInfoInternal import android.hardware.biometrics.SensorLocationInternal import android.hardware.biometrics.SensorProperties +import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor @@ -78,6 +79,7 @@ class FakeFingerprintManagerInteractor : FingerprintManagerInteractor { override suspend fun enroll( hardwareAuthToken: ByteArray?, enrollReason: EnrollReason, + fingerprintEnrollOptions: FingerprintEnrollOptions ): Flow = flowOf(*enrollStateViewModel.toTypedArray()) override suspend fun removeFingerprint(fp: FingerprintData): Boolean { 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 900afd1d55d..67a5957b9f5 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 @@ -109,12 +109,7 @@ class FingerprintManagerInteractorTest { fingerprintManager, fingerprintSensorRepository, gateKeeperPasswordProvider, - FingerprintEnrollInteractorImpl( - context, - FingerprintEnrollOptions.Builder().build(), - fingerprintManager, - Default, - ), + FingerprintEnrollInteractorImpl(context, fingerprintManager, Default), ) } @@ -135,7 +130,7 @@ class FingerprintManagerInteractorTest { whenever(fingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(fingerprintList) val list = underTest.enrolledFingerprints.last() - assertThat(list.size).isEqualTo(fingerprintList.size) + assertThat(list!!.size).isEqualTo(fingerprintList.size) val actual = list[0] assertThat(actual.name).isEqualTo(expected.name) assertThat(actual.fingerId).isEqualTo(expected.biometricId) @@ -318,7 +313,11 @@ class FingerprintManagerInteractorTest { testScope.runTest { val token = byteArrayOf(5, 3, 2) var result: FingerEnrollState? = null - val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } } + val job = launch { + underTest + .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) + .collect { result = it } + } val enrollCallback: ArgumentCaptor = argumentCaptor() runCurrent() @@ -343,7 +342,11 @@ class FingerprintManagerInteractorTest { testScope.runTest { val token = byteArrayOf(5, 3, 2) var result: FingerEnrollState? = null - val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } } + val job = launch { + underTest + .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) + .collect { result = it } + } val enrollCallback: ArgumentCaptor = argumentCaptor() runCurrent() @@ -368,7 +371,11 @@ class FingerprintManagerInteractorTest { testScope.runTest { val token = byteArrayOf(5, 3, 2) var result: FingerEnrollState? = null - val job = launch { underTest.enroll(token, EnrollReason.FindSensor).collect { result = it } } + val job = launch { + underTest + .enroll(token, EnrollReason.FindSensor, FingerprintEnrollOptions.Builder().build()) + .collect { result = it } + } val enrollCallback: ArgumentCaptor = argumentCaptor() runCurrent() 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 4906e845f87..9662c39d94a 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 @@ -35,7 +35,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel import com.android.settings.testutils2.FakeFingerprintManagerInteractor import com.android.systemui.biometrics.shared.model.toFingerprintSensor @@ -90,45 +89,20 @@ class FingerprintEnrollFindSensorViewModelV2Test { Dispatchers.setMain(backgroundDispatcher) fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor() - gatekeeperViewModel = - FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory( - null, - fakeFingerprintManagerInteractor, - ) - .create(FingerprintGatekeeperViewModel::class.java) + gatekeeperViewModel = FingerprintGatekeeperViewModel(fakeFingerprintManagerInteractor) - val sensor = - FingerprintSensorPropertiesInternal( - 0 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 5 /* maxEnrollmentsPerUser */, - listOf(), - FingerprintSensorProperties.TYPE_POWER_BUTTON, - false /* halControlsIllumination */, - true /* resetLockoutRequiresHardwareAuthToken */, - listOf(SensorLocationInternal.DEFAULT), - ) - .toFingerprintSensor() + val fingerprintFlowViewModel = FingerprintFlowViewModel() + fingerprintFlowViewModel.updateFlowType(Default) + navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor) - val fingerprintFlowViewModel = FingerprintFlowViewModel(Default) - navigationViewModel = - FingerprintNavigationViewModel( - FingerprintNavigationStep.Education(sensor), - false, - fingerprintFlowViewModel, - fakeFingerprintManagerInteractor, - ) - - backgroundViewModel = - BackgroundViewModel.BackgroundViewModelFactory().create(BackgroundViewModel::class.java) + backgroundViewModel = BackgroundViewModel() backgroundViewModel.inForeground() enrollViewModel = - FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory( - fakeFingerprintManagerInteractor, - gatekeeperViewModel, - navigationViewModel, - ) - .create(FingerprintEnrollViewModel::class.java) + FingerprintEnrollViewModel( + fakeFingerprintManagerInteractor, + gatekeeperViewModel, + navigationViewModel, + ) accessibilityInteractor = object : AccessibilityInteractor { override val isAccessibilityEnabled: Flow = flowOf(false) @@ -145,23 +119,23 @@ class FingerprintEnrollFindSensorViewModelV2Test { orientationInteractor = object : OrientationInteractor { override val orientation: Flow = flowOf(Configuration.ORIENTATION_LANDSCAPE) - override val rotation: Flow = flowOf(Surface.ROTATION_0) - override val rotationFromDefault: Flow = flowOf(Surface.ROTATION_0) + override val rotation: Flow = flowOf(Surface.ROTATION_0) + override val rotationFromDefault: Flow = flowOf(Surface.ROTATION_0) + override fun getRotationFromDefault(rotation: Int): Int = rotation } underTest = - FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory( - navigationViewModel, - enrollViewModel, - gatekeeperViewModel, - backgroundViewModel, - accessibilityInteractor, - foldStateInteractor, - orientationInteractor, - fingerprintFlowViewModel, - fakeFingerprintManagerInteractor, - ) - .create(FingerprintEnrollFindSensorViewModel::class.java) + FingerprintEnrollFindSensorViewModel( + navigationViewModel, + enrollViewModel, + gatekeeperViewModel, + backgroundViewModel, + fingerprintFlowViewModel, + accessibilityInteractor, + foldStateInteractor, + orientationInteractor, + fakeFingerprintManagerInteractor, + ) } @After diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt index ad025cfb1c3..f59d1fcb820 100644 --- a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt +++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt @@ -23,22 +23,23 @@ import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.android.settings.biometrics.fingerprint2.lib.model.Default +import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel import com.android.settings.testutils2.FakeFingerprintManagerInteractor -import com.android.systemui.biometrics.shared.model.FingerprintSensor -import com.android.systemui.biometrics.shared.model.FingerprintSensorType -import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.biometrics.shared.model.toFingerprintSensor import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -53,44 +54,70 @@ class FingerprintEnrollConfirmationViewModelTest { @get:Rule val instantTaskRule = InstantTaskExecutorRule() private var backgroundDispatcher = StandardTestDispatcher() private var testScope = TestScope(backgroundDispatcher) - val fingerprintFlowViewModel = FingerprintFlowViewModel(Default) + val fingerprintFlowViewModel = FingerprintFlowViewModel() val fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor() lateinit var navigationViewModel: FingerprintNavigationViewModel lateinit var underTest: FingerprintEnrollConfirmationViewModel @Before fun setup() { - navigationViewModel = - FingerprintNavigationViewModel( - FingerprintNavigationStep.Confirmation, - false, - fingerprintFlowViewModel, - fakeFingerprintManagerInteractor, - ) + Dispatchers.setMain(backgroundDispatcher) + fingerprintFlowViewModel.updateFlowType(Default) + navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor) underTest = FingerprintEnrollConfirmationViewModel(navigationViewModel, fakeFingerprintManagerInteractor) + navigationViewModel.updateFingerprintFlow(Default) + navigationViewModel.hasConfirmedDeviceCredential(true) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + private fun bringToConfirmation() { + navigationViewModel.update( + FingerprintAction.NEXT, + FingerprintNavigationStep.Introduction::class, + "Intro.Test.NEXT", + ) + navigationViewModel.update( + FingerprintAction.NEXT, + FingerprintNavigationStep.Education::class, + "Edu.Test.NEXT", + ) + navigationViewModel.update( + FingerprintAction.NEXT, + FingerprintNavigationStep.Enrollment::class, + "Enrollment.Test.NEXT", + ) } @Test fun testCanEnrollFingerprints() = testScope.runTest { - fakeFingerprintManagerInteractor.sensorProp = FingerprintSensorPropertiesInternal( - 0 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 5 /* maxEnrollmentsPerUser */, - listOf(), - FingerprintSensorProperties.TYPE_POWER_BUTTON, - false /* halControlsIllumination */, - true /* resetLockoutRequiresHardwareAuthToken */, - listOf(SensorLocationInternal.DEFAULT), - ) - .toFingerprintSensor() + advanceUntilIdle() + bringToConfirmation() + fakeFingerprintManagerInteractor.sensorProp = + FingerprintSensorPropertiesInternal( + 0 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 5 /* maxEnrollmentsPerUser */, + listOf(), + FingerprintSensorProperties.TYPE_POWER_BUTTON, + false /* halControlsIllumination */, + true /* resetLockoutRequiresHardwareAuthToken */, + listOf(SensorLocationInternal.DEFAULT), + ) + .toFingerprintSensor() fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = mutableListOf() fakeFingerprintManagerInteractor.enrollableFingerprints = 5 var canEnrollFingerprints: Boolean = false - val job = launch { underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it } } + val job = launch { + underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it } + } advanceUntilIdle() assertThat(canEnrollFingerprints).isTrue() @@ -100,12 +127,14 @@ class FingerprintEnrollConfirmationViewModelTest { @Test fun testNextButtonSendsNextStep() = testScope.runTest { + advanceUntilIdle() + bringToConfirmation() var step: FingerprintNavigationStep.UiStep? = null val job = launch { navigationViewModel.navigateTo.collect { step = it } } underTest.onNextButtonClicked() - runCurrent() + advanceUntilIdle() assertThat(step).isNull() job.cancel() @@ -114,14 +143,18 @@ class FingerprintEnrollConfirmationViewModelTest { @Test fun testAddAnotherSendsAction() = testScope.runTest { + advanceUntilIdle() + bringToConfirmation() + advanceUntilIdle() + var step: FingerprintNavigationStep.UiStep? = null val job = launch { navigationViewModel.navigateTo.collect { step = it } } underTest.onAddAnotherButtonClicked() - runCurrent() + advanceUntilIdle() - assertThat(step).isInstanceOf(FingerprintNavigationStep.Enrollment::class.java) + assertThat(step).isNull() job.cancel() } } diff --git a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt index 5a304330a16..c475cc47faa 100644 --- a/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt +++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollEnrollingViewModelTest.kt @@ -28,9 +28,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintFlowViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel -import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo import com.android.settings.testutils2.FakeFingerprintManagerInteractor import com.android.systemui.biometrics.shared.model.toFingerprintSensor import com.google.common.truth.Truth.assertThat @@ -61,20 +59,15 @@ class FingerprintEnrollEnrollingViewModelTest { private lateinit var backgroundViewModel: BackgroundViewModel private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel private lateinit var navigationViewModel: FingerprintNavigationViewModel - private val defaultGatekeeperInfo = GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 3), 3) private var testScope = TestScope(backgroundDispatcher) private lateinit var fakeFingerprintManagerInteractor: FakeFingerprintManagerInteractor - private fun initialize(gatekeeperInfo: GatekeeperInfo = defaultGatekeeperInfo) { + private fun initialize() { fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor() - gateKeeperViewModel = - FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory( - gatekeeperInfo, - fakeFingerprintManagerInteractor, - ) - .create(FingerprintGatekeeperViewModel::class.java) - val sensor = + + gateKeeperViewModel = FingerprintGatekeeperViewModel(fakeFingerprintManagerInteractor) + fakeFingerprintManagerInteractor.sensorProp = FingerprintSensorPropertiesInternal( 1 /* sensorId */, SensorProperties.STRENGTH_STRONG, @@ -86,32 +79,21 @@ class FingerprintEnrollEnrollingViewModelTest { listOf(SensorLocationInternal.DEFAULT), ) .toFingerprintSensor() - val fingerprintFlowViewModel = FingerprintFlowViewModel(Default) + val fingerprintFlowViewModel = FingerprintFlowViewModel() + fingerprintFlowViewModel.updateFlowType(Default) - navigationViewModel = - FingerprintNavigationViewModel( - Enrollment(sensor), - false, - fingerprintFlowViewModel, - fakeFingerprintManagerInteractor, - ) + navigationViewModel = FingerprintNavigationViewModel(fakeFingerprintManagerInteractor) - backgroundViewModel = - BackgroundViewModel.BackgroundViewModelFactory().create(BackgroundViewModel::class.java) + backgroundViewModel = BackgroundViewModel() backgroundViewModel.inForeground() val fingerprintEnrollViewModel = - FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory( - fakeFingerprintManagerInteractor, - gateKeeperViewModel, - navigationViewModel, - ) - .create(FingerprintEnrollViewModel::class.java) + FingerprintEnrollViewModel( + fakeFingerprintManagerInteractor, + gateKeeperViewModel, + navigationViewModel, + ) enrollEnrollingViewModel = - FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingViewModelFactory( - fingerprintEnrollViewModel, - backgroundViewModel, - ) - .create(FingerprintEnrollEnrollingViewModel::class.java) + FingerprintEnrollEnrollingViewModel(fingerprintEnrollViewModel, backgroundViewModel) } @Before @@ -128,6 +110,7 @@ class FingerprintEnrollEnrollingViewModelTest { @Test fun testEnrollShouldBeFalse() = testScope.runTest { + gateKeeperViewModel.onConfirmDevice(true, 3L, false) var shouldEnroll = false val job = launch { @@ -147,6 +130,7 @@ class FingerprintEnrollEnrollingViewModelTest { @Test fun testEnrollShouldBeFalseWhenBackground() = testScope.runTest { + gateKeeperViewModel.onConfirmDevice(true, 3L, false) var shouldEnroll = false val job = launch {