Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0fe0f4d9 authored by Joshua McCloskey's avatar Joshua McCloskey
Browse files

Adding FingerprintEnrollConfirmation

Bug: 295217067
Test: atest FingerprintEnrollConfirmationScreenshotTest.kt
Test: adb shell device_config put biometrics_framework com.android.settings.flags.fingerprint_v2_enrollment true
Change-Id: I0b4995a97b7bccd35e26ed89bbbf14b80af4b0e1
parent a9178952
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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"
}
+24 −1
Original line number Diff line number Diff line
@@ -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(
+79 −3
Original line number Diff line number Diff line
@@ -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()
  }
}
+15 −4
Original line number Diff line number Diff line
@@ -47,10 +47,12 @@ class RFPSViewModel(
  /** Value to indicate if the text view is visible or not */
  val textViewIsVisible: Flow<Boolean> = _textViewIsVisible.asStateFlow()

  private var _shouldAnimateIcon: Flow<Boolean> =
    fingerprintEnrollViewModel.enrollFlowShouldBeRunning
  /** Indicates if the icon should be animating or not */
  val shouldAnimateIcon = fingerprintEnrollViewModel.enrollFlowShouldBeRunning
  val shouldAnimateIcon = _shouldAnimateIcon

  private val enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFLow
  private var enrollFlow: Flow<FingerEnrollState?> = 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<FingerEnrollState.EnrollProgress?> =
    enrollFlow
      .filterIsInstance<FingerEnrollState.EnrollProgress>()
      .shareIn(viewModelScope, SharingStarted.Eagerly, 1)
      .shareIn(viewModelScope, SharingStarted.Eagerly, 0)

  /** Clear help message on enroll progress */
  val clearHelpMessage: Flow<Boolean> = 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,
+67 −0
Original line number Diff line number Diff line
/*
 * 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<Boolean> = 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 <T : ViewModel> create(modelClass: Class<T>): T {
      return FingerprintEnrollConfirmationViewModel(navigationViewModel, fingerprintInteractor) as T
    }
  }

  companion object {
    private const val TAG = "FingerprintEnrollConfirmationViewModel"
    private val navStep = FingerprintNavigationStep.Confirmation::class
  }
}
Loading