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

Commit 913e7149 authored by Hao Dong's avatar Hao Dong Committed by Android (Google) Code Review
Browse files

Merge "Implement FingerprintEnrollFindSensorV2Fragment" into main

parents 08710ee5 5f48cbaf
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -30,10 +30,10 @@ import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;

import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;

import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;

/** Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment. */
@@ -95,7 +95,7 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
        return dialog;
    }

    public static void showErrorDialog(BiometricEnrollBase host, int errMsgId, boolean isSetup) {
    public static void showErrorDialog(FragmentActivity host, int errMsgId, boolean isSetup) {
        if (host.isFinishing()) {
            return;
        }
+56 −4
Original line number Diff line number Diff line
@@ -20,11 +20,13 @@ import android.annotation.ColorInt
import android.app.Activity
import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Color
import android.hardware.fingerprint.FingerprintManager
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.accessibility.AccessibilityManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
@@ -44,17 +46,21 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.Finge
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Confirmation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Enrollment
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Finish
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Intro
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.LaunchConfirmDeviceCredential
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
import com.android.settings.password.ChooseLockGeneric
import com.android.settings.password.ChooseLockSettingsHelper
import com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE
@@ -72,6 +78,10 @@ private const val TAG = "FingerprintEnrollmentV2Activity"
class FingerprintEnrollmentV2Activity : FragmentActivity() {
  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
  private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
  private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
  private lateinit var accessibilityViewModel: AccessibilityViewModel
  private lateinit var foldStateViewModel: FoldStateViewModel
  private lateinit var orientationStateViewModel: OrientationStateViewModel
  private val coroutineDispatcher = Dispatchers.Default

  /** Result listener for ChooseLock activity flow. */
@@ -94,6 +104,11 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
    super.onAttachedToWindow()
  }

  override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    foldStateViewModel.onConfigurationChange(newConfig)
  }

  @ColorInt
  private fun getBackgroundColor(): Int {
    val stateList: ColorStateList? =
@@ -178,16 +193,53 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
        )
      )[FingerprintEnrollNavigationViewModel::class.java]

    // Initialize FoldStateViewModel
    foldStateViewModel =
      ViewModelProvider(this, FoldStateViewModel.FoldStateViewModelFactory(context))[
        FoldStateViewModel::class.java]
    foldStateViewModel.onConfigurationChange(resources.configuration)

    // Initialize FingerprintViewModel
    fingerprintEnrollViewModel =
      ViewModelProvider(
        this,
      FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(interactor, backgroundDispatcher)
        FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
          interactor,
          backgroundDispatcher
        )
      )[FingerprintEnrollViewModel::class.java]

    // Initialize scroll view model
    ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
      FingerprintScrollViewModel::class.java]

    // Initialize AccessibilityViewModel
    accessibilityViewModel =
      ViewModelProvider(
        this,
        AccessibilityViewModel.AccessibilityViewModelFactory(
          getSystemService(AccessibilityManager::class.java)!!
        )
      )[AccessibilityViewModel::class.java]

    // Initialize OrientationViewModel
    orientationStateViewModel =
      ViewModelProvider(this, OrientationStateViewModel.OrientationViewModelFactory(context))[
        OrientationStateViewModel::class.java]

    // Initialize FingerprintEnrollFindSensorViewModel
    ViewModelProvider(
      this,
      FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
        navigationViewModel,
        fingerprintEnrollViewModel,
        gatekeeperViewModel,
        accessibilityViewModel,
        foldStateViewModel,
        orientationStateViewModel
      )
    )[FingerprintEnrollFindSensorViewModel::class.java]

    lifecycleScope.launch {
      navigationViewModel.navigationViewModel.filterNotNull().collect {
        Log.d(TAG, "navigationStep $it")
+2 −1
Original line number Diff line number Diff line
@@ -19,10 +19,11 @@ package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel

/** A fragment that is responsible for enrolling a users fingerprint. */
class FingerprintEnrollEnrollingV2Fragment : Fragment() {
class FingerprintEnrollEnrollingV2Fragment : Fragment(R.layout.fingerprint_enroll_enrolling) {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
+176 −5
Original line number Diff line number Diff line
@@ -17,26 +17,197 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.airbnb.lottie.LottieAnimationView
import com.android.settings.R
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

private const val TAG = "FingerprintEnrollFindSensorV2Fragment"

/**
 * A fragment that is used to educate the user about the fingerprint sensor on this device.
 *
 * If the sensor is not a udfps sensor, this fragment listens to fingerprint enrollment for
 * proceeding to the enroll enrolling.
 *
 * The main goals of this page are
 * 1. Inform the user where the fingerprint sensor is on their device
 * 2. Explain to the user how the enrollment process shown by [FingerprintEnrollEnrollingV2Fragment]
 *    will work.
 */
class FingerprintEnrollFindSensorV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_find_sensor) {
class FingerprintEnrollFindSensorV2Fragment : Fragment() {
  // This is only for non-udfps or non-sfps sensor. For udfps and sfps, we show lottie.
  private var animation: FingerprintFindSensorAnimation? = null

  private var contentLayoutId: Int = -1
  private lateinit var viewModel: FingerprintEnrollFindSensorViewModel

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (savedInstanceState == null) {
      val navigationViewModel =
        ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
    viewModel =
      ViewModelProvider(requireActivity())[FingerprintEnrollFindSensorViewModel::class.java]
    lifecycleScope.launch {
      viewModel.sensorType.collect {
        contentLayoutId =
          when (it) {
            FingerprintSensorType.UDFPS_OPTICAL,
            FingerprintSensorType.UDFPS_ULTRASONIC -> R.layout.udfps_enroll_find_sensor_layout
            FingerprintSensorType.POWER_BUTTON -> R.layout.sfps_enroll_find_sensor_layout
            else -> R.layout.fingerprint_v2_enroll_find_sensor
          }
      }
    }
  }

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View? {
    return inflater.inflate(contentLayoutId, container, false).also { it ->
      val view = it!! as GlifLayout

      // Set up header and description
      lifecycleScope.launch { viewModel.sensorType.collect { setTexts(it, view) } }

      // Set up footer bar
      val footerBarMixin = view.getMixin(FooterBarMixin::class.java)
      setupSecondaryButton(footerBarMixin)
      lifecycleScope.launch {
        viewModel.showPrimaryButton.collect { setupPrimaryButton(footerBarMixin) }
      }

      // Set up lottie or animation
      lifecycleScope.launch {
        viewModel.showSfpsLottie.collect { (isFolded, rotation) ->
          setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
        }
      }
      lifecycleScope.launch {
        viewModel.showUdfpsLottie.collect { isAccessibilityEnabled ->
          val lottieAnimation =
            if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie
          setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
        }
      }
      lifecycleScope.launch {
        viewModel.showRfpsAnimation.collect {
          animation = view.findViewById(R.id.fingerprint_sensor_location_animation)
          animation!!.startAnimation()
        }
      }

      lifecycleScope.launch {
        viewModel.showErrorDialog.collect { (errMsgId, isSetup) ->
          // TODO: Covert error dialog kotlin as well
          FingerprintErrorDialog.showErrorDialog(requireActivity(), errMsgId, isSetup)
        }
      }
    }
  }

  override fun onDestroy() {
    animation?.stopAnimation()
    super.onDestroy()
  }

  private fun setupSecondaryButton(footerBarMixin: FooterBarMixin) {
    footerBarMixin.secondaryButton =
      FooterButton.Builder(requireActivity())
        .setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
        .setListener {
          run {
            // TODO: Show the dialog for suw
            Log.d(TAG, "onSkipClicked")
            // TODO: Finish activity in the root activity instead.
            requireActivity().finish()
          }
        }
        .setButtonType(FooterButton.ButtonType.SKIP)
        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
        .build()
  }

  private fun setupPrimaryButton(footerBarMixin: FooterBarMixin) {
    footerBarMixin.primaryButton =
      FooterButton.Builder(requireActivity())
        .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
        .setListener {
          run {
            Log.d(TAG, "onStartButtonClick")
            viewModel.proceedToEnrolling()
          }
        }
        .setButtonType(FooterButton.ButtonType.NEXT)
        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
        .build()
  }

  private fun setupLottie(
    view: View,
    lottieAnimation: Int,
    lottieClickListener: View.OnClickListener? = null
  ) {
    val illustrationLottie: LottieAnimationView? = view.findViewById(R.id.illustration_lottie)
    illustrationLottie?.setAnimation(lottieAnimation)
    illustrationLottie?.playAnimation()
    illustrationLottie?.setOnClickListener(lottieClickListener)
    illustrationLottie?.visibility = View.VISIBLE
  }

  private fun setTexts(sensorType: FingerprintSensorType, view: GlifLayout) {
    when (sensorType) {
      FingerprintSensorType.UDFPS_OPTICAL,
      FingerprintSensorType.UDFPS_ULTRASONIC -> {
        view.setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title)
        view.setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message)
      }
      FingerprintSensorType.POWER_BUTTON -> {
        view.setHeaderText(R.string.security_settings_sfps_enroll_find_sensor_title)
        view.setDescriptionText(R.string.security_settings_sfps_enroll_find_sensor_message)
      }
      else -> {
        view.setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title)
        view.setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message)
      }
    }
  }

  private fun getSfpsIllustrationLottieAnimation(isFolded: Boolean, rotation: Int): Int {
    val animation: Int
    when (rotation) {
      Surface.ROTATION_90 ->
        animation =
          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_left
          else R.raw.fingerprint_edu_lottie_portrait_top_left)
      Surface.ROTATION_180 ->
        animation =
          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_left
          else R.raw.fingerprint_edu_lottie_landscape_bottom_left)
      Surface.ROTATION_270 ->
        animation =
          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_bottom_right
          else R.raw.fingerprint_edu_lottie_portrait_bottom_right)
      else ->
        animation =
          (if (isFolded) R.raw.fingerprint_edu_lottie_folded_top_right
          else R.raw.fingerprint_edu_lottie_landscape_top_right)
    }
    return animation
  }
}
+4 −1
Original line number Diff line number Diff line
@@ -180,7 +180,10 @@ class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll
    scrollView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
    // Next button responsible for starting the next fragment.
    val onNextButtonClick: View.OnClickListener =
      View.OnClickListener { Log.d(TAG, "OnNextClicked") }
      View.OnClickListener {
        Log.d(TAG, "OnNextClicked")
        navigationViewModel.nextStep()
      }

    val layout: GlifLayout = requireActivity().requireViewById(R.id.setup_wizard_layout)
    footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
Loading