diff --git a/aconfig/settings_biometrics_framework_flag_declarations.aconfig b/aconfig/settings_biometrics_framework_flag_declarations.aconfig index e9f19bc4ff4..4355ed146f7 100644 --- a/aconfig/settings_biometrics_framework_flag_declarations.aconfig +++ b/aconfig/settings_biometrics_framework_flag_declarations.aconfig @@ -7,3 +7,10 @@ flag { description: "This flag enables or disables the BiometricSettingsProvider" bug: "303595205" } + +flag { + name: "fingerprint_v2_enrollment" + namespace: "biometrics_framework" + description: "This flag enables or disables the Fingerprint v2 enrollment" + bug: "295206723" +} 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 65030de3f07..d26b812a686 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 @@ -37,13 +37,13 @@ import com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED import com.android.settings.biometrics.GatekeeperPasswordProvider import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepositoryImpl -import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractorImpl import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractorImpl +import com.android.settings.biometrics.fingerprint2.domain.interactor.PressToAuthInteractorImpl import com.android.settings.biometrics.fingerprint2.lib.model.Default import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment @@ -54,6 +54,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.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 @@ -70,6 +71,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing 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.flags.Flags import com.android.settings.password.ChooseLockGeneric import com.android.settings.password.ChooseLockSettingsHelper import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE @@ -96,6 +98,8 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel private lateinit var backgroundViewModel: BackgroundViewModel private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel + private lateinit var fingerprintEnrollConfirmationViewModel: + FingerprintEnrollConfirmationViewModel private val coroutineDispatcher = Dispatchers.Default /** Result listener for ChooseLock activity flow. */ @@ -155,6 +159,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { // TODO(b/299573056): Show split screen dialog when it's in multi window mode. setContentView(R.layout.fingerprint_v2_enroll_main) + if (!Flags.fingerprintV2Enrollment()) { + check(false) { + "fingerprint enrollment v2 is not enabled, " + + "please run adb shell device_config put " + + "biometrics_framework com.android.settings.flags.fingerprint_v2_enrollment true" + } + finish() + } + setTheme(SetupWizardUtils.getTheme(applicationContext, intent)) ThemeHelper.trySetDynamicColor(applicationContext) @@ -293,6 +306,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { ), )[RFPSViewModel::class.java] + fingerprintEnrollConfirmationViewModel = + ViewModelProvider( + this, + FingerprintEnrollConfirmationViewModel.FingerprintEnrollConfirmationViewModelFactory( + navigationViewModel, + fingerprintManagerInteractor, + ), + )[FingerprintEnrollConfirmationViewModel::class.java] + lifecycleScope.launch { navigationViewModel.currentStep.collect { step -> if (step is Init) { @@ -304,6 +326,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { lifecycleScope.launch { navigationViewModel.navigateTo.filterNotNull().collect { step -> + Log.d(TAG, "navigateTo: $step") if (step is ConfirmDeviceCredential) { launchConfirmOrChooseLock(userId) navigationViewModel.update( 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 fd07a952056..d8c1c93f910 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 @@ -17,7 +17,21 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment 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.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.ui.enrollment.viewmodel.FingerprintEnrollConfirmationViewModel +import com.google.android.setupcompat.template.FooterBarMixin +import com.google.android.setupcompat.template.FooterButton +import com.google.android.setupdesign.GlifLayout +import kotlinx.coroutines.launch /** * A fragment to indicate that fingerprint enrollment has been completed. @@ -25,9 +39,71 @@ import androidx.fragment.app.Fragment * This page will display basic information about what a fingerprint can be used for and acts as the * final step of enrollment. */ -class FingerprintEnrollConfirmationV2Fragment : Fragment() { +class FingerprintEnrollConfirmationV2Fragment() : + Fragment(R.layout.fingerprint_enroll_finish_base) { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + 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] + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? = + super.onCreateView(inflater, container, savedInstanceState).also { theView -> + val mainView = theView!! as GlifLayout + + mainView.setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title) + mainView.setDescriptionText(R.string.security_settings_fingerprint_enroll_finish_v2_message) + + val mixin = mainView.getMixin(FooterBarMixin::class.java) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.isAddAnotherButtonVisible.collect { + mixin.secondaryButton = + FooterButton.Builder(requireContext()) + .setText(R.string.fingerprint_enroll_button_add) + .setListener { viewModel.onAddAnotherButtonClicked() } + .setButtonType(FooterButton.ButtonType.SKIP) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary) + .build() + } + } + } + + mixin.setPrimaryButton( + FooterButton.Builder(requireContext()) + .setText(R.string.security_settings_fingerprint_enroll_done) + .setListener(this::onNextButtonClick) + .setButtonType(FooterButton.ButtonType.NEXT) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) + .build() + ) + } + + @Suppress("UNUSED_PARAMETER") + private fun onNextButtonClick(view: View?) { + viewModel.onNextButtonClicked() } } 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 2408a8873a8..6ee57099ea2 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 @@ -47,10 +47,12 @@ class RFPSViewModel( /** Value to indicate if the text view is visible or not */ val textViewIsVisible: Flow = _textViewIsVisible.asStateFlow() + private var _shouldAnimateIcon: Flow = + fingerprintEnrollViewModel.enrollFlowShouldBeRunning /** Indicates if the icon should be animating or not */ - val shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning + val shouldAnimateIcon = _shouldAnimateIcon - private val enrollFlow: Flow = fingerprintEnrollViewModel.enrollFLow + private var enrollFlow: Flow = fingerprintEnrollViewModel.enrollFLow /** * Enroll progress message with a replay of size 1 allowing for new subscribers to get the most @@ -59,7 +61,7 @@ class RFPSViewModel( val progress: Flow = enrollFlow .filterIsInstance() - .shareIn(viewModelScope, SharingStarted.Eagerly, 1) + .shareIn(viewModelScope, SharingStarted.Eagerly, 0) /** Clear help message on enroll progress */ val clearHelpMessage: Flow = progress.map { it != null } @@ -122,6 +124,7 @@ class RFPSViewModel( /** Indicates the negative button has been clicked */ fun negativeButtonClicked() { + doReset() navigationViewModel.update( FingerprintAction.NEGATIVE_BUTTON_PRESSED, navStep, @@ -129,11 +132,19 @@ class RFPSViewModel( ) } - /** Indicates that enrollment has been finished and we can proceed to the next step. */ + /** Indicates that an enrollment was completed */ fun finishedSuccessfully() { + doReset() navigationViewModel.update(FingerprintAction.NEXT, navStep, "${TAG}#progressFinished") } + private fun doReset() { + _textViewIsVisible.update { false } + _shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning + /** Indicates if the icon should be animating or not */ + enrollFlow = fingerprintEnrollViewModel.enrollFLow + } + class RFPSViewModelFactory( private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel, private val navigationViewModel: FingerprintNavigationViewModel, 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 new file mode 100644 index 00000000000..d9b31d7f622 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModel.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor +import kotlinx.coroutines.flow.Flow + +/** + * Models the UI state for [FingerprintEnrollConfirmationV2Fragment] + */ +class FingerprintEnrollConfirmationViewModel( + private val navigationViewModel: FingerprintNavigationViewModel, + fingerprintInteractor: FingerprintManagerInteractor, +) : ViewModel() { + + /** + * Indicates if the add another button is possible. This should only be true when the user is able + * to enroll more fingerprints. + */ + val isAddAnotherButtonVisible: Flow = fingerprintInteractor.canEnrollFingerprints + + /** + * Indicates that the user has clicked the next button and is done with fingerprint enrollment. + */ + fun onNextButtonClicked() { + navigationViewModel.update(FingerprintAction.NEXT, navStep, "onNextButtonClicked") + } + + /** + * Indicates that the user has clicked the add another button and will be sent to the enrollment + * screen. + */ + fun onAddAnotherButtonClicked() { + 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 + } +} diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt index 979f9533e0f..76b4895f2bb 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintNavigationStep.kt @@ -37,6 +37,7 @@ enum class FingerprintAction { ACTIVITY_CREATED, NEGATIVE_BUTTON_PRESSED, USER_CLICKED_FINISH, + ADD_ANOTHER, } /** State that can be used to help a [FingerprintNavigationStep] determine the next step to take. */ @@ -179,6 +180,7 @@ sealed interface FingerprintNavigationStep { return when (action) { FingerprintAction.NEXT -> Finish(null) FingerprintAction.PREV -> TransitionStep(Education(state.fingerprintSensor!!)) + FingerprintAction.ADD_ANOTHER -> TransitionStep(Enrollment(state.fingerprintSensor!!)) else -> null } } 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 26c20cff8af..131f5bb1e96 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 @@ -89,7 +89,7 @@ class FingerprintNavigationViewModel( fun update(action: FingerprintAction, caller: KClass<*>, debugStr: String) { Log.d(TAG, "$caller.update($action) $debugStr") val currentStep = _currentStep.value - val isUiStep = currentStep is UiStep + val isUiStep = currentStep is UiStep && caller is UiStep if (currentStep == null) { throw NullPointerException("current step is null") } diff --git a/tests/screenshot/OWNERS b/tests/screenshot/OWNERS new file mode 100644 index 00000000000..fbe29d186c1 --- /dev/null +++ b/tests/screenshot/OWNERS @@ -0,0 +1,2 @@ +joshmccloskey@google.com +jbolinger@google.com diff --git a/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png b/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png new file mode 100644 index 00000000000..db9009be8b7 Binary files /dev/null and b/tests/screenshot/assets/robolectric/fp_enroll_confirmation.png differ 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 84d76ff5501..3cd2002d396 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 @@ -27,6 +27,7 @@ import com.android.settings.biometrics.fingerprint2.lib.model.Default import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel +import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.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 @@ -103,6 +104,9 @@ class Injector(step: FingerprintNavigationStep.UiStep) { var rfpsViewModel = RFPSViewModel(fingerprintEnrollEnrollingViewModel, navigationViewModel, orientationInteractor) + val fingerprintEnrollConfirmationViewModel = + FingerprintEnrollConfirmationViewModel(navigationViewModel, interactor) + var fingerprintFindSensorViewModel = FingerprintEnrollFindSensorViewModel( navigationViewModel, @@ -131,6 +135,7 @@ class Injector(step: FingerprintNavigationStep.UiStep) { BackgroundViewModel::class.java -> backgroundViewModel RFPSIconTouchViewModel::class.java -> rfpsIconTouchViewModel FingerprintEnrollEnrollingViewModel::class.java -> fingerprintEnrollEnrollingViewModel + FingerprintEnrollConfirmationViewModel::class.java -> fingerprintEnrollConfirmationViewModel else -> null } as T 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/FingerprintEnrollConfirmationScreenshotTest.kt new file mode 100644 index 00000000000..28f4fbec5d5 --- /dev/null +++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/fragment/FingerprintEnrollConfirmationScreenshotTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.tests.screenshot.biometrics.fingerprint.fragment + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import 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 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import platform.test.screenshot.FragmentScreenshotTestRule +import platform.test.screenshot.ViewScreenshotTestRule.Mode + +@RunWith(AndroidJUnit4::class) +class FingerprintEnrollConfirmationScreenshotTest { + private val injector: Injector = Injector(FingerprintNavigationStep.Confirmation) + + @Rule + @JvmField + var rule: FragmentScreenshotTestRule = Injector.BiometricFragmentScreenShotRule() + + @Test + fun testConfirmation() { + rule.screenshotTest( + "fp_enroll_confirmation", + Mode.MatchSize, + FingerprintEnrollConfirmationV2Fragment(injector.factory), + ) + } +} 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 f202c4fb7c3..829716e0e2d 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 @@ -40,6 +40,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -119,21 +120,18 @@ class FingerprintEnrollFindSensorViewModelV2Test { } foldStateInteractor = object : FoldStateInteractor { - override val isFolded: Flow = foldState + override val isFolded: Flow = foldState.asStateFlow() + override fun onConfigurationChange(newConfig: Configuration) { - TODO("Not yet implemented") + foldState.update { false } } } + orientationInteractor = - object: OrientationInteractor { - override val orientation: Flow - get() = TODO("Not yet implemented") + object : OrientationInteractor { + override val orientation: Flow = flowOf(Configuration.ORIENTATION_LANDSCAPE) override val rotation: Flow = flowOf(Surface.ROTATION_0) - - override fun getRotationFromDefault(rotation: Int): Int { - TODO("Not yet implemented") - } - + override fun getRotationFromDefault(rotation: Int): Int = rotation } underTest = FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory( 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 new file mode 100644 index 00000000000..696e4fa6bbb --- /dev/null +++ b/tests/unit/src/com/android/settings/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollConfirmationViewModelTest.kt @@ -0,0 +1,111 @@ +/* + * 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.fingerprint2.ui.enrollment.viewmodel + +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.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.google.common.truth.Truth.assertThat +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.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class FingerprintEnrollConfirmationViewModelTest { + @JvmField @Rule var rule = MockitoJUnit.rule() + + @get:Rule val instantTaskRule = InstantTaskExecutorRule() + private var backgroundDispatcher = StandardTestDispatcher() + private var testScope = TestScope(backgroundDispatcher) + val fingerprintFlowViewModel = FingerprintFlowViewModel(Default) + val fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor() + lateinit var navigationViewModel: FingerprintNavigationViewModel + lateinit var underTest: FingerprintEnrollConfirmationViewModel + + @Before + fun setup() { + navigationViewModel = + FingerprintNavigationViewModel( + FingerprintNavigationStep.Confirmation, + false, + fingerprintFlowViewModel, + fakeFingerprintManagerInteractor, + ) + underTest = + FingerprintEnrollConfirmationViewModel(navigationViewModel, fakeFingerprintManagerInteractor) + } + + @Test + fun testCanEnrollFingerprints() = + testScope.runTest { + fakeFingerprintManagerInteractor.sensorProp = + FingerprintSensor(0 /* sensorId */, SensorStrength.STRONG, 5, FingerprintSensorType.REAR) + fakeFingerprintManagerInteractor.enrolledFingerprintsInternal = mutableListOf() + fakeFingerprintManagerInteractor.enrollableFingerprints = 5 + + var canEnrollFingerprints: Boolean = false + val job = launch { underTest.isAddAnotherButtonVisible.collect { canEnrollFingerprints = it } } + + advanceUntilIdle() + assertThat(canEnrollFingerprints).isTrue() + job.cancel() + } + + @Test + fun testNextButtonSendsNextStep() = + testScope.runTest { + var step: FingerprintNavigationStep.UiStep? = null + val job = launch { navigationViewModel.navigateTo.collect { step = it } } + + underTest.onNextButtonClicked() + + runCurrent() + + assertThat(step).isNull() + job.cancel() + } + + @Test + fun testAddAnotherSendsAction() = + testScope.runTest { + var step: FingerprintNavigationStep.UiStep? = null + val job = launch { navigationViewModel.navigateTo.collect { step = it } } + + underTest.onAddAnotherButtonClicked() + + runCurrent() + + assertThat(step).isInstanceOf(FingerprintNavigationStep.Enrollment::class.java) + job.cancel() + } +}