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

Commit 5f48cbaf authored by Hao Dong's avatar Hao Dong
Browse files

Implement FingerprintEnrollFindSensorV2Fragment

Bug: 295206773
Test: N/A

Change-Id: I5ddbdc925d6aefd0994016449d5baa5feae3de6b
parent 71235aa4
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