[BiometricsV2] Add retry button

Add retry button for FingerprintEnrollErrorDialog and make sure that
this button works well in the whole enrollment flow.

Bug: 287168522
Test: manually test this dialog with error and rotate devices
Test: atest FingerprintEnrollEnrollingViewModelTest
Test: atest FingerprintEnrollErrorDialogViewModelTest
Test: atest FingerprintEnrollProgressViewModelTest
Test: atest FingerprintEnrollmentActivityTest
Test: atest biometrics-enrollment-test

Change-Id: Ica1d91d077ca322caca5551068f2a3c23b544361
This commit is contained in:
Milton Wu
2023-06-28 21:51:53 +08:00
parent a372258805
commit f94932801a
21 changed files with 1138 additions and 729 deletions

View File

@@ -77,6 +77,7 @@ android_library {
"setupcompat", "setupcompat",
"setupdesign", "setupdesign",
"androidx.lifecycle_lifecycle-runtime", "androidx.lifecycle_lifecycle-runtime",
"androidx.lifecycle_lifecycle-runtime-ktx",
"androidx.lifecycle_lifecycle-viewmodel", "androidx.lifecycle_lifecycle-viewmodel",
"guava", "guava",
"jsr305", "jsr305",

View File

@@ -70,25 +70,7 @@
app:lottie_loop="true" app:lottie_loop="true"
app:lottie_speed=".85" /> app:lottie_speed=".85" />
<com.android.settings.biometrics2.ui.widget.UdfpsEnrollView <include layout="@layout/udfps_enroll_enrolling_v2_udfps_view"/>
android:id="@+id/udfps_animation_view"
android:layout_width="218.42dp"
android:layout_height="216dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="553dp">
<ImageView
android:id="@+id/udfps_enroll_animation_fp_progress_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Fingerprint -->
<ImageView
android:id="@+id/udfps_enroll_animation_fp_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.settings.biometrics2.ui.widget.UdfpsEnrollView>
<Button <Button
style="@style/SudGlifButton.Secondary" style="@style/SudGlifButton.Secondary"

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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.
-->
<com.android.settings.biometrics2.ui.widget.UdfpsEnrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="218.42dp"
android:layout_height="216dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="553dp">
<ImageView
android:id="@+id/udfps_enroll_animation_fp_progress_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Fingerprint -->
<ImageView
android:id="@+id/udfps_enroll_animation_fp_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.settings.biometrics2.ui.widget.UdfpsEnrollView>

View File

@@ -34,6 +34,7 @@ import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.Cha
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel; import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel;
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel; import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel; import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel; import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel; import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel;
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel; import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel;
@@ -47,7 +48,7 @@ import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
*/ */
public class BiometricsViewModelFactory implements ViewModelProvider.Factory { public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
private static final String TAG = "BiometricsViewModelFact"; private static final String TAG = "BiometricsViewModelFactory";
public static final CreationExtras.Key<ChallengeGenerator> CHALLENGE_GENERATOR_KEY = public static final CreationExtras.Key<ChallengeGenerator> CHALLENGE_GENERATOR_KEY =
new CreationExtras.Key<ChallengeGenerator>() {}; new CreationExtras.Key<ChallengeGenerator>() {};
@@ -113,7 +114,7 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
final Integer userId = extras.get(USER_ID_KEY); final Integer userId = extras.get(USER_ID_KEY);
final FingerprintRepository fingerprint = provider.getFingerprintRepository( final FingerprintRepository fingerprint = provider.getFingerprintRepository(
application); application);
if (fingerprint != null) { if (fingerprint != null && userId != null) {
return (T) new FingerprintEnrollEnrollingViewModel(application, userId, return (T) new FingerprintEnrollEnrollingViewModel(application, userId,
fingerprint); fingerprint);
} }
@@ -122,10 +123,15 @@ public class BiometricsViewModelFactory implements ViewModelProvider.Factory {
final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY); final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY);
final FingerprintRepository fingerprint = provider.getFingerprintRepository( final FingerprintRepository fingerprint = provider.getFingerprintRepository(
application); application);
if (fingerprint != null && userId != null) { if (fingerprint != null && userId != null && request != null) {
return (T) new FingerprintEnrollFinishViewModel(application, userId, request, return (T) new FingerprintEnrollFinishViewModel(application, userId, request,
fingerprint); fingerprint);
} }
} else if (modelClass.isAssignableFrom(FingerprintEnrollErrorDialogViewModel.class)) {
final EnrollmentRequest request = extras.get(ENROLLMENT_REQUEST_KEY);
if (request != null) {
return (T) new FingerprintEnrollErrorDialogViewModel(application, request.isSuw());
}
} }
return create(modelClass); return create(modelClass);
} }

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2023 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.biometrics2.ui.view
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.hardware.biometrics.BiometricConstants
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import com.android.settings.R
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
/**
* Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment.
*/
class FingerprintEnrollEnrollingErrorDialog : DialogFragment() {
private var mViewModel: FingerprintEnrollEnrollingViewModel? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val value = mViewModel!!.errorDialogLiveData.value!!
return requireActivity().bindFingerprintEnrollEnrollingErrorDialog(
title = value.errTitle,
message = value.errMsg,
positiveButtonClickListener = { dialog: DialogInterface?, _: Int ->
dialog?.dismiss()
mViewModel?.onErrorDialogAction(
if (value.errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT)
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
else
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
)
}
)
}
override fun onAttach(context: Context) {
mViewModel = ViewModelProvider(requireActivity())[
FingerprintEnrollEnrollingViewModel::class.java]
super.onAttach(context)
}
}
fun Context.bindFingerprintEnrollEnrollingErrorDialog(
title: CharSequence?,
message: CharSequence?,
positiveButtonClickListener: DialogInterface.OnClickListener
): AlertDialog = AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_ok,
positiveButtonClickListener
)
.create()
.apply { setCanceledOnTouchOutside(false) }

View File

@@ -23,13 +23,14 @@ import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable import android.graphics.drawable.LayerDrawable
import android.hardware.fingerprint.FingerprintManager import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_CANCELED import android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Surface
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AnimationUtils.loadInterpolator import android.view.animation.AnimationUtils.loadInterpolator
@@ -39,17 +40,22 @@ import android.widget.TextView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics2.ui.model.EnrollmentProgress import com.android.settings.biometrics2.ui.model.EnrollmentProgress
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.launch
/** /**
* Fragment is used to handle enrolling process for rfps * Fragment is used to handle enrolling process for rfps
@@ -64,25 +70,24 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
private val progressViewModel: FingerprintEnrollProgressViewModel private val progressViewModel: FingerprintEnrollProgressViewModel
get() = _progressViewModel!! get() = _progressViewModel!!
private val fastOutSlowInInterpolator: Interpolator private var _errorDialogViewModel: FingerprintEnrollErrorDialogViewModel? = null
get() = loadInterpolator(requireActivity(), android.R.interpolator.fast_out_slow_in) private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel
get() = _errorDialogViewModel!!
private val linearOutSlowInInterpolator: Interpolator private var fastOutSlowInInterpolator: Interpolator? = null
get() = loadInterpolator(requireActivity(), android.R.interpolator.linear_out_slow_in) private var linearOutSlowInInterpolator: Interpolator? = null
private var fastOutLinearInInterpolator: Interpolator? = null
private val fastOutLinearInInterpolator: Interpolator
get() = loadInterpolator(requireActivity(), android.R.interpolator.fast_out_linear_in)
private var isAnimationCancelled = false private var isAnimationCancelled = false
private var enrollingRfpsView: GlifLayout? = null private var enrollingView: GlifLayout? = null
private val progressBar: ProgressBar private val progressBar: ProgressBar
get() = enrollingRfpsView!!.findViewById<ProgressBar>(R.id.fingerprint_progress_bar)!! get() = enrollingView!!.findViewById(R.id.fingerprint_progress_bar)!!
private var progressAnim: ObjectAnimator? = null private var progressAnim: ObjectAnimator? = null
private val errorText: TextView private val errorText: TextView
get() = enrollingRfpsView!!.findViewById<TextView>(R.id.error_text)!! get() = enrollingView!!.findViewById(R.id.error_text)!!
private val iconAnimationDrawable: AnimatedVectorDrawable? private val iconAnimationDrawable: AnimatedVectorDrawable?
get() = (progressBar.background as LayerDrawable) get() = (progressBar.background as LayerDrawable)
@@ -94,11 +99,10 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
private var iconTouchCount = 0 private var iconTouchCount = 0
private val touchAgainRunnable = private val touchAgainRunnable = Runnable {
Runnable {
showError( showError(
// Use enrollingRfpsView to getString to prevent activity is missing during rotation // Use enrollingView to getString to prevent activity is missing during rotation
enrollingRfpsView!!.context.getString( enrollingView!!.context.getString(
R.string.security_settings_fingerprint_enroll_lift_touch_again R.string.security_settings_fingerprint_enroll_lift_touch_again
) )
) )
@@ -106,41 +110,36 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
private val onSkipClickListener = View.OnClickListener { _: View? -> private val onSkipClickListener = View.OnClickListener { _: View? ->
enrollingViewModel.setOnSkipPressed() enrollingViewModel.setOnSkipPressed()
cancelEnrollment() cancelEnrollment(true)
} }
private val progressObserver: Observer<EnrollmentProgress> = private var enrollingCancelSignal: Any? = null
Observer<EnrollmentProgress> { progress: EnrollmentProgress? ->
if (DEBUG) { private val progressObserver = Observer { progress: EnrollmentProgress? ->
Log.d(TAG, "progressObserver($progress)")
}
if (progress != null && progress.steps >= 0) { if (progress != null && progress.steps >= 0) {
onEnrollmentProgressChange(progress) onEnrollmentProgressChange(progress)
} }
} }
private val helpMessageObserver: Observer<EnrollmentStatusMessage> = private val helpMessageObserver = Observer { helpMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { helpMessage: EnrollmentStatusMessage? ->
if (DEBUG) {
Log.d(TAG, "helpMessageObserver($helpMessage)")
}
helpMessage?.let { onEnrollmentHelp(it) } helpMessage?.let { onEnrollmentHelp(it) }
} }
private val errorMessageObserver: Observer<EnrollmentStatusMessage> = private val errorMessageObserver = Observer { errorMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { errorMessage: EnrollmentStatusMessage? ->
if (DEBUG) {
Log.d(TAG, "errorMessageObserver($errorMessage)") Log.d(TAG, "errorMessageObserver($errorMessage)")
}
errorMessage?.let { onEnrollmentError(it) } errorMessage?.let { onEnrollmentError(it) }
} }
private val canceledSignalObserver = Observer { canceledSignal: Any? ->
canceledSignal?.let { onEnrollmentCanceled(it) }
}
private val onBackPressedCallback: OnBackPressedCallback = private val onBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) { object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
isEnabled = false isEnabled = false
enrollingViewModel.setOnBackPressed() enrollingViewModel.setOnBackPressed()
cancelEnrollment() cancelEnrollment(true)
} }
} }
@@ -148,6 +147,7 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
ViewModelProvider(requireActivity()).let { provider -> ViewModelProvider(requireActivity()).let { provider ->
_enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java] _enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java]
_progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java] _progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java]
_errorDialogViewModel = provider[FingerprintEnrollErrorDialogViewModel::class.java]
} }
super.onAttach(context) super.onAttach(context)
requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback) requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback)
@@ -162,10 +162,10 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
enrollingRfpsView = inflater.inflate( enrollingView = inflater.inflate(
R.layout.fingerprint_enroll_enrolling, container, false R.layout.fingerprint_enroll_enrolling, container, false
) as GlifLayout ) as GlifLayout
return enrollingRfpsView!! return enrollingView!!
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -193,16 +193,46 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
} }
requireActivity().bindFingerprintEnrollEnrollingRfpsView( requireActivity().bindFingerprintEnrollEnrollingRfpsView(
view = enrollingRfpsView!!, view = enrollingView!!,
onSkipClickListener = onSkipClickListener onSkipClickListener = onSkipClickListener
) )
fastOutSlowInInterpolator =
loadInterpolator(requireContext(), android.R.interpolator.fast_out_slow_in)
linearOutSlowInInterpolator =
loadInterpolator(requireContext(), android.R.interpolator.linear_out_slow_in)
fastOutLinearInInterpolator =
loadInterpolator(requireContext(), android.R.interpolator.fast_out_linear_in)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.triggerRetryFlow.collect { retryEnrollment() }
}
}
}
private fun retryEnrollment() {
isAnimationCancelled = false
startIconAnimation()
startEnrollment()
clearError()
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
updateTitleAndDescription()
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val isEnrolling = progressViewModel.isEnrolling
val isErrorDialogShown = errorDialogViewModel.isDialogShown
Log.d(TAG, "onStart(), isEnrolling:$isEnrolling, isErrorDialog:$isErrorDialogShown")
if (!isErrorDialogShown) {
isAnimationCancelled = false isAnimationCancelled = false
startIconAnimation() startIconAnimation()
startEnrollment() startEnrollment()
}
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!) updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
updateTitleAndDescription() updateTitleAndDescription()
} }
@@ -219,32 +249,44 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
override fun onStop() { override fun onStop() {
stopIconAnimation() stopIconAnimation()
removeEnrollmentObservers() removeEnrollmentObservers()
if (!activity!!.isChangingConfigurations && progressViewModel.isEnrolling) { val isEnrolling = progressViewModel.isEnrolling
progressViewModel.cancelEnrollment() val isConfigChange = requireActivity().isChangingConfigurations
Log.d(TAG, "onStop(), enrolling:$isEnrolling isConfigChange:$isConfigChange")
if (isEnrolling && !isConfigChange) {
cancelEnrollment(false)
} }
super.onStop() super.onStop()
} }
private fun removeEnrollmentObservers() { private fun removeEnrollmentObservers() {
preRemoveEnrollmentObservers()
progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver) progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver)
}
private fun preRemoveEnrollmentObservers() {
progressViewModel.progressLiveData.removeObserver(progressObserver) progressViewModel.progressLiveData.removeObserver(progressObserver)
progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver) progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver)
} }
private fun cancelEnrollment() { private fun cancelEnrollment(waitForLastCancelErrMsg: Boolean) {
preRemoveEnrollmentObservers() if (!progressViewModel.isEnrolling) {
progressViewModel.cancelEnrollment() Log.d(TAG, "cancelEnrollment(), failed because isEnrolling is false")
return
}
removeEnrollmentObservers()
if (waitForLastCancelErrMsg) {
progressViewModel.canceledSignalLiveData.observe(this, canceledSignalObserver)
} else {
enrollingCancelSignal = null
}
val cancelResult: Boolean = progressViewModel.cancelEnrollment()
if (!cancelResult) {
Log.e(TAG, "cancelEnrollment(), failed to cancel enrollment")
}
} }
private fun startEnrollment() { private fun startEnrollment() {
val startResult: Boolean = enrollingCancelSignal = progressViewModel.startEnrollment(ENROLL_ENROLL)
progressViewModel.startEnrollment(FingerprintManager.ENROLL_ENROLL) if (enrollingCancelSignal == null) {
if (!startResult) {
Log.e(TAG, "startEnrollment(), failed") Log.e(TAG, "startEnrollment(), failed")
} else {
Log.d(TAG, "startEnrollment(), success")
} }
progressViewModel.progressLiveData.observe(this, progressObserver) progressViewModel.progressLiveData.observe(this, progressObserver)
progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver) progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver)
@@ -252,6 +294,7 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
} }
private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) { private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) {
Log.d(TAG, "onEnrollmentHelp($helpMessage)")
val helpStr: CharSequence = helpMessage.str val helpStr: CharSequence = helpMessage.str
if (!TextUtils.isEmpty(helpStr)) { if (!TextUtils.isEmpty(helpStr)) {
errorText.removeCallbacks(touchAgainRunnable) errorText.removeCallbacks(touchAgainRunnable)
@@ -261,29 +304,27 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) { private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) {
stopIconAnimation() stopIconAnimation()
removeEnrollmentObservers()
if (enrollingViewModel.onBackPressed cancelEnrollment(true)
&& errorMessage.msgId == FINGERPRINT_ERROR_CANCELED lifecycleScope.launch {
) { Log.d(TAG, "newDialog $errorMessage")
errorDialogViewModel.newDialog(errorMessage.msgId)
}
}
private fun onEnrollmentCanceled(canceledSignal: Any) {
Log.d(
TAG,
"onEnrollmentCanceled enrolling:$enrollingCancelSignal, canceled:$canceledSignal"
)
if (enrollingCancelSignal === canceledSignal) {
progressViewModel.canceledSignalLiveData.removeObserver(canceledSignalObserver)
progressViewModel.clearProgressLiveData()
if (enrollingViewModel.onBackPressed) {
enrollingViewModel.onCancelledDueToOnBackPressed() enrollingViewModel.onCancelledDueToOnBackPressed()
} else if (enrollingViewModel.onSkipPressed } else if (enrollingViewModel.onSkipPressed) {
&& errorMessage.msgId == FINGERPRINT_ERROR_CANCELED
) {
enrollingViewModel.onCancelledDueToOnSkipPressed() enrollingViewModel.onCancelledDueToOnSkipPressed()
} else { }
val errMsgId: Int = errorMessage.msgId
enrollingViewModel.showErrorDialog(
FingerprintEnrollEnrollingViewModel.ErrorDialogData(
enrollingRfpsView!!.context.getString(
FingerprintErrorDialog.getErrorMessage(errMsgId)
),
enrollingRfpsView!!.context.getString(
FingerprintErrorDialog.getErrorTitle(errMsgId)
),
errMsgId
)
)
progressViewModel.cancelEnrollment()
} }
} }
@@ -296,11 +337,10 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
} }
private fun updateProgress(animate: Boolean, enrollmentProgress: EnrollmentProgress) { private fun updateProgress(animate: Boolean, enrollmentProgress: EnrollmentProgress) {
if (!progressViewModel.isEnrolling) {
Log.d(TAG, "Enrollment not started yet")
return
}
val progress = getProgress(enrollmentProgress) val progress = getProgress(enrollmentProgress)
Log.d(TAG, "updateProgress($animate, $enrollmentProgress), old:${progressBar.progress}"
+ ", new:$progress")
// Only clear the error when progress has been made. // Only clear the error when progress has been made.
// TODO (b/234772728) Add tests. // TODO (b/234772728) Add tests.
if (progressBar.progress < progress) { if (progressBar.progress < progress) {
@@ -328,7 +368,7 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
errorText.text = error errorText.text = error
if (errorText.visibility == View.INVISIBLE) { if (errorText.visibility == View.INVISIBLE) {
errorText.visibility = View.VISIBLE errorText.visibility = View.VISIBLE
errorText.translationY = enrollingRfpsView!!.context.resources.getDimensionPixelSize( errorText.translationY = enrollingView!!.context.resources.getDimensionPixelSize(
R.dimen.fingerprint_error_text_appear_distance R.dimen.fingerprint_error_text_appear_distance
).toFloat() ).toFloat()
errorText.alpha = 0f errorText.alpha = 0f
@@ -359,7 +399,7 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
) )
.setDuration(100) .setDuration(100)
.setInterpolator(fastOutLinearInInterpolator) .setInterpolator(fastOutLinearInInterpolator)
.withEndAction { errorText!!.visibility = View.INVISIBLE } .withEndAction { errorText.visibility = View.INVISIBLE }
.start() .start()
} }
} }
@@ -385,8 +425,8 @@ class FingerprintEnrollEnrollingRfpsFragment : Fragment() {
private fun updateTitleAndDescription() { private fun updateTitleAndDescription() {
val progressLiveData: EnrollmentProgress = progressViewModel.progressLiveData.value!! val progressLiveData: EnrollmentProgress = progressViewModel.progressLiveData.value!!
GlifLayoutHelper(activity!!, enrollingRfpsView!!).setDescriptionText( GlifLayoutHelper(activity!!, enrollingView!!).setDescriptionText(
enrollingRfpsView!!.context.getString( enrollingView!!.context.getString(
if (progressLiveData.steps == -1) if (progressLiveData.steps == -1)
R.string.security_settings_fingerprint_enroll_start_message R.string.security_settings_fingerprint_enroll_start_message
else else

View File

@@ -21,10 +21,11 @@ import android.annotation.RawRes
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.ColorFilter
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.hardware.fingerprint.FingerprintManager import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_CANCELED
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -39,24 +40,29 @@ import android.widget.RelativeLayout
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieComposition import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieProperty import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.model.KeyPath import com.airbnb.lottie.model.KeyPath
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics2.ui.model.EnrollmentProgress import com.android.settings.biometrics2.ui.model.EnrollmentProgress
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout import com.google.android.setupdesign.GlifLayout
import com.google.android.setupdesign.template.DescriptionMixin import com.google.android.setupdesign.template.DescriptionMixin
import com.google.android.setupdesign.template.HeaderMixin import com.google.android.setupdesign.template.HeaderMixin
import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
@@ -72,13 +78,17 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
private val progressViewModel: FingerprintEnrollProgressViewModel private val progressViewModel: FingerprintEnrollProgressViewModel
get() = _progressViewModel!! get() = _progressViewModel!!
private var _errorDialogViewModel: FingerprintEnrollErrorDialogViewModel? = null
private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel
get() = _errorDialogViewModel!!
private val fastOutSlowInInterpolator: Interpolator private val fastOutSlowInInterpolator: Interpolator
get() = AnimationUtils.loadInterpolator(activity, R.interpolator.fast_out_slow_in) get() = AnimationUtils.loadInterpolator(activity, R.interpolator.fast_out_slow_in)
private var enrollingSfpsView: GlifLayout? = null private var enrollingView: GlifLayout? = null
private val progressBar: ProgressBar private val progressBar: ProgressBar
get() = enrollingSfpsView!!.findViewById<ProgressBar>(R.id.fingerprint_progress_bar)!! get() = enrollingView!!.findViewById(R.id.fingerprint_progress_bar)!!
private var progressAnim: ObjectAnimator? = null private var progressAnim: ObjectAnimator? = null
@@ -96,7 +106,7 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
private val illustrationLottie: LottieAnimationView private val illustrationLottie: LottieAnimationView
get() = enrollingSfpsView!!.findViewById<LottieAnimationView>(R.id.illustration_lottie)!! get() = enrollingView!!.findViewById(R.id.illustration_lottie)!!
private var haveShownSfpsNoAnimationLottie = false private var haveShownSfpsNoAnimationLottie = false
private var haveShownSfpsCenterLottie = false private var haveShownSfpsCenterLottie = false
@@ -110,78 +120,82 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
private val showIconTouchDialogRunnable = Runnable { showIconTouchDialog() } private val showIconTouchDialogRunnable = Runnable { showIconTouchDialog() }
private var enrollingCancelSignal: Any? = null
// Give the user a chance to see progress completed before jumping to the next stage. // Give the user a chance to see progress completed before jumping to the next stage.
private val delayedFinishRunnable = Runnable { enrollingViewModel.onEnrollingDone() } private val delayedFinishRunnable = Runnable { enrollingViewModel.onEnrollingDone() }
private val onSkipClickListener = View.OnClickListener { _: View? -> private val onSkipClickListener = View.OnClickListener { _: View? ->
enrollingViewModel.setOnSkipPressed() enrollingViewModel.setOnSkipPressed()
cancelEnrollment() cancelEnrollment(true)
} }
private val progressObserver: Observer<EnrollmentProgress> = private val progressObserver = Observer { progress: EnrollmentProgress? ->
Observer<EnrollmentProgress> { progress: EnrollmentProgress? ->
if (DEBUG) {
Log.d(TAG, "progressObserver($progress)")
}
if (progress != null && progress.steps >= 0) { if (progress != null && progress.steps >= 0) {
onEnrollmentProgressChange(progress) onEnrollmentProgressChange(progress)
} }
} }
private val helpMessageObserver: Observer<EnrollmentStatusMessage> = private val helpMessageObserver = Observer { helpMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { helpMessage: EnrollmentStatusMessage? ->
if (DEBUG) {
Log.d(TAG, "helpMessageObserver($helpMessage)")
}
helpMessage?.let { onEnrollmentHelp(it) } helpMessage?.let { onEnrollmentHelp(it) }
} }
private val errorMessageObserver: Observer<EnrollmentStatusMessage> = private val errorMessageObserver = Observer { errorMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { errorMessage: EnrollmentStatusMessage? ->
if (DEBUG) {
Log.d(TAG, "errorMessageObserver($errorMessage)") Log.d(TAG, "errorMessageObserver($errorMessage)")
}
errorMessage?.let { onEnrollmentError(it) } errorMessage?.let { onEnrollmentError(it) }
} }
private val canceledSignalObserver = Observer { canceledSignal: Any? ->
Log.d(TAG, "canceledSignalObserver($canceledSignal)")
canceledSignal?.let { onEnrollmentCanceled(it) }
}
private val onBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
isEnabled = false
enrollingViewModel.setOnBackPressed()
cancelEnrollment(true)
}
}
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
ViewModelProvider(requireActivity()).let { provider -> ViewModelProvider(requireActivity()).let { provider ->
_enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java] _enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java]
_progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java] _progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java]
_errorDialogViewModel = provider[FingerprintEnrollErrorDialogViewModel::class.java]
} }
super.onAttach(context) super.onAttach(context)
requireActivity().onBackPressedDispatcher.addCallback( requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback)
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
isEnabled = false
enrollingViewModel.setOnBackPressed()
cancelEnrollment()
} }
})
override fun onDetach() {
onBackPressedCallback.isEnabled = false
super.onDetach()
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
enrollingSfpsView = inflater.inflate( enrollingView = inflater.inflate(
R.layout.sfps_enroll_enrolling, R.layout.sfps_enroll_enrolling,
container, false container, false
) as GlifLayout ) as GlifLayout
return enrollingSfpsView return enrollingView
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
requireActivity().bindFingerprintEnrollEnrollingSfpsView( requireActivity().bindFingerprintEnrollEnrollingSfpsView(
view = enrollingSfpsView!!, view = enrollingView!!,
onSkipClickListener = onSkipClickListener onSkipClickListener = onSkipClickListener
) )
// setHelpAnimation() // setHelpAnimation()
helpAnimation = ObjectAnimator.ofFloat( helpAnimation = ObjectAnimator.ofFloat(
enrollingSfpsView!!.findViewById<RelativeLayout>(R.id.progress_lottie)!!, enrollingView!!.findViewById<RelativeLayout>(R.id.progress_lottie)!!,
"translationX" /* propertyName */, "translationX" /* propertyName */,
0f, 0f,
HELP_ANIMATION_TRANSLATION_X, HELP_ANIMATION_TRANSLATION_X,
@@ -212,48 +226,79 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
true true
} }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.triggerRetryFlow.collect { retryEnrollment() }
}
}
}
private fun retryEnrollment() {
startEnrollment()
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val isEnrolling = progressViewModel.isEnrolling
val isErrorDialogShown = errorDialogViewModel.isDialogShown
Log.d(TAG, "onStart(), isEnrolling:$isEnrolling, isErrorDialog:$isErrorDialogShown")
if (!isErrorDialogShown) {
startEnrollment() startEnrollment()
}
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!) updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
progressViewModel.helpMessageLiveData.value?.let { progressViewModel.helpMessageLiveData.value.let {
if (it != null) {
onEnrollmentHelp(it) onEnrollmentHelp(it)
} ?: run { } else {
clearError() clearError()
updateTitleAndDescription() updateTitleAndDescription()
} }
} }
}
override fun onStop() { override fun onStop() {
removeEnrollmentObservers() removeEnrollmentObservers()
if (!activity!!.isChangingConfigurations && progressViewModel.isEnrolling) { val isEnrolling = progressViewModel.isEnrolling
progressViewModel.cancelEnrollment() val isConfigChange = requireActivity().isChangingConfigurations
Log.d(TAG, "onStop(), enrolling:$isEnrolling isConfigChange:$isConfigChange")
if (isEnrolling && !isConfigChange) {
cancelEnrollment(false)
} }
super.onStop() super.onStop()
} }
private fun removeEnrollmentObservers() { private fun removeEnrollmentObservers() {
preRemoveEnrollmentObservers()
progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver) progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver)
}
private fun preRemoveEnrollmentObservers() {
progressViewModel.progressLiveData.removeObserver(progressObserver) progressViewModel.progressLiveData.removeObserver(progressObserver)
progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver) progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver)
} }
private fun cancelEnrollment() { private fun cancelEnrollment(waitForLastCancelErrMsg: Boolean) {
preRemoveEnrollmentObservers() if (!progressViewModel.isEnrolling) {
progressViewModel.cancelEnrollment() Log.d(TAG, "cancelEnrollment(), failed because isEnrolling is false")
return
}
removeEnrollmentObservers()
if (waitForLastCancelErrMsg) {
progressViewModel.canceledSignalLiveData.observe(this, canceledSignalObserver)
} else {
enrollingCancelSignal = null
}
val cancelResult: Boolean = progressViewModel.cancelEnrollment()
if (!cancelResult) {
Log.e(TAG, "cancelEnrollment(), failed to cancel enrollment")
}
} }
private fun startEnrollment() { private fun startEnrollment() {
val startResult: Boolean = enrollingCancelSignal = progressViewModel.startEnrollment(ENROLL_ENROLL)
progressViewModel.startEnrollment(FingerprintManager.ENROLL_ENROLL) if (enrollingCancelSignal == null) {
if (!startResult) {
Log.e(TAG, "startEnrollment(), failed") Log.e(TAG, "startEnrollment(), failed")
} else {
Log.d(TAG, "startEnrollment(), success")
} }
progressViewModel.progressLiveData.observe(this, progressObserver) progressViewModel.progressLiveData.observe(this, progressObserver)
progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver) progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver)
@@ -261,7 +306,7 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
private fun configureEnrollmentStage(description: CharSequence, @RawRes lottie: Int) { private fun configureEnrollmentStage(description: CharSequence, @RawRes lottie: Int) {
GlifLayoutHelper(requireActivity(), enrollingSfpsView!!).setDescriptionText(description) GlifLayoutHelper(requireActivity(), enrollingView!!).setDescriptionText(description)
LottieCompositionFactory.fromRawRes(activity, lottie) LottieCompositionFactory.fromRawRes(activity, lottie)
.addListener { c: LottieComposition -> .addListener { c: LottieComposition ->
illustrationLottie.setComposition(c) illustrationLottie.setComposition(c)
@@ -290,6 +335,7 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) { private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) {
Log.d(TAG, "onEnrollmentHelp($helpMessage)")
val helpStr: CharSequence = helpMessage.str val helpStr: CharSequence = helpMessage.str
if (helpStr.isNotEmpty()) { if (helpStr.isNotEmpty()) {
showError(helpStr) showError(helpStr)
@@ -297,25 +343,26 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) { private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) {
removeEnrollmentObservers() cancelEnrollment(true)
if (enrollingViewModel.onBackPressed lifecycleScope.launch {
&& errorMessage.msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED Log.d(TAG, "newDialog $errorMessage")
) { errorDialogViewModel.newDialog(errorMessage.msgId)
}
}
private fun onEnrollmentCanceled(canceledSignal: Any) {
Log.d(
TAG,
"onEnrollmentCanceled enrolling:$enrollingCancelSignal, canceled:$canceledSignal"
)
if (enrollingCancelSignal === canceledSignal) {
progressViewModel.canceledSignalLiveData.removeObserver(canceledSignalObserver)
progressViewModel.clearProgressLiveData()
if (enrollingViewModel.onBackPressed) {
enrollingViewModel.onCancelledDueToOnBackPressed() enrollingViewModel.onCancelledDueToOnBackPressed()
} else if (enrollingViewModel.onSkipPressed } else if (enrollingViewModel.onSkipPressed) {
&& errorMessage.msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
) {
enrollingViewModel.onCancelledDueToOnSkipPressed() enrollingViewModel.onCancelledDueToOnSkipPressed()
} else { }
val errMsgId: Int = errorMessage.msgId
enrollingViewModel.showErrorDialog(
FingerprintEnrollEnrollingViewModel.ErrorDialogData(
getString(FingerprintErrorDialog.getErrorMessage(errMsgId)),
getString(FingerprintErrorDialog.getErrorTitle(errMsgId)),
errMsgId
)
)
progressViewModel.cancelEnrollment()
} }
} }
@@ -345,6 +392,8 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
val progress = getProgress(enrollmentProgress) val progress = getProgress(enrollmentProgress)
Log.d(TAG, "updateProgress($animate, $enrollmentProgress), old:${progressBar.progress}"
+ ", new:$progress")
// Only clear the error when progress has been made. // Only clear the error when progress has been made.
// TODO (b/234772728) Add tests. // TODO (b/234772728) Add tests.
@@ -365,12 +414,12 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
if (progress.steps == -1) { if (progress.steps == -1) {
return 0 return 0
} }
val displayProgress = Math.max(0, progress.steps + 1 - progress.remaining) val displayProgress = 0.coerceAtLeast(progress.steps + 1 - progress.remaining)
return PROGRESS_BAR_MAX * displayProgress / (progress.steps + 1) return PROGRESS_BAR_MAX * displayProgress / (progress.steps + 1)
} }
private fun showError(error: CharSequence) { private fun showError(error: CharSequence) {
enrollingSfpsView!!.let { enrollingView!!.let {
it.headerText = error it.headerText = error
it.headerTextView.contentDescription = error it.headerTextView.contentDescription = error
GlifLayoutHelper(requireActivity(), it).setDescriptionText("") GlifLayoutHelper(requireActivity(), it).setDescriptionText("")
@@ -425,7 +474,7 @@ class FingerprintEnrollEnrollingSfpsFragment : Fragment() {
} }
private fun updateTitleAndDescription() { private fun updateTitleAndDescription() {
val helper = GlifLayoutHelper(requireActivity(), enrollingSfpsView!!) val helper = GlifLayoutHelper(requireActivity(), enrollingView!!)
if (enrollingViewModel.isAccessibilityEnabled) { if (enrollingViewModel.isAccessibilityEnabled) {
enrollingViewModel.clearTalkback() enrollingViewModel.clearTalkback()
helper.glifLayout.descriptionTextView.accessibilityLiveRegion = helper.glifLayout.descriptionTextView.accessibilityLiveRegion =
@@ -584,7 +633,7 @@ private fun ProgressBar.applyProgressBarDynamicColor(context: Context, isError:
} }
fun LottieAnimationView.applyLottieDynamicColor(context: Context, isError: Boolean) { fun LottieAnimationView.applyLottieDynamicColor(context: Context, isError: Boolean) {
addValueCallback<ColorFilter>( addValueCallback(
KeyPath(".blue100", "**"), KeyPath(".blue100", "**"),
LottieProperty.COLOR_FILTER LottieProperty.COLOR_FILTER
) { ) {

View File

@@ -17,8 +17,8 @@ package com.android.settings.biometrics2.ui.view
import android.annotation.RawRes import android.annotation.RawRes
import android.content.Context import android.content.Context
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL import android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_CANCELED
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@@ -35,20 +35,25 @@ import android.widget.TextView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieComposition import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory import com.airbnb.lottie.LottieCompositionFactory
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog
import com.android.settings.biometrics2.ui.model.EnrollmentProgress import com.android.settings.biometrics2.ui.model.EnrollmentProgress
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.android.settings.biometrics2.ui.widget.UdfpsEnrollView import com.android.settings.biometrics2.ui.widget.UdfpsEnrollView
import com.android.settingslib.display.DisplayDensityUtils import com.android.settingslib.display.DisplayDensityUtils
import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
@@ -68,6 +73,10 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
private val progressViewModel: FingerprintEnrollProgressViewModel private val progressViewModel: FingerprintEnrollProgressViewModel
get() = _progressViewModel!! get() = _progressViewModel!!
private var _errorDialogViewModel: FingerprintEnrollErrorDialogViewModel? = null
private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel
get() = _errorDialogViewModel!!
private var illustrationLottie: LottieAnimationView? = null private var illustrationLottie: LottieAnimationView? = null
private var haveShownTipLottie = false private var haveShownTipLottie = false
@@ -76,22 +85,22 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
private var haveShownCenterLottie = false private var haveShownCenterLottie = false
private var haveShownGuideLottie = false private var haveShownGuideLottie = false
private var enrollingUdfpsView: RelativeLayout? = null private var enrollingView: RelativeLayout? = null
private val titleText: TextView private val titleText: TextView
get() = enrollingUdfpsView!!.findViewById<TextView>(R.id.suc_layout_title)!! get() = enrollingView!!.findViewById(R.id.suc_layout_title)!!
private val subTitleText: TextView private val subTitleText: TextView
get() = enrollingUdfpsView!!.findViewById<TextView>(R.id.sud_layout_subtitle)!! get() = enrollingView!!.findViewById(R.id.sud_layout_subtitle)!!
private val udfpsEnrollView: UdfpsEnrollView private val udfpsEnrollView: UdfpsEnrollView
get() = enrollingUdfpsView!!.findViewById<UdfpsEnrollView>(R.id.udfps_animation_view)!! get() = enrollingView!!.findViewById(R.id.udfps_animation_view)!!
private val skipBtn: Button private val skipBtn: Button
get() = enrollingUdfpsView!!.findViewById<Button>(R.id.skip_btn)!! get() = enrollingView!!.findViewById(R.id.skip_btn)!!
private val icon: ImageView private val icon: ImageView
get() = enrollingUdfpsView!!.findViewById<ImageView>(R.id.sud_layout_icon)!! get() = enrollingView!!.findViewById(R.id.sud_layout_icon)!!
private val shouldShowLottie: Boolean private val shouldShowLottie: Boolean
get() { get() {
@@ -108,25 +117,34 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
private var rotation = -1 private var rotation = -1
private var enrollingCancelSignal: Any? = null
private val onSkipClickListener = View.OnClickListener { _: View? -> private val onSkipClickListener = View.OnClickListener { _: View? ->
enrollingViewModel.setOnSkipPressed() enrollingViewModel.setOnSkipPressed()
cancelEnrollment() cancelEnrollment(false)
} }
private val progressObserver: Observer<EnrollmentProgress> = private val progressObserver = Observer { progress: EnrollmentProgress? ->
Observer<EnrollmentProgress> { progress: EnrollmentProgress? -> if (progress != null && progress.steps >= 0) {
progress?.let { onEnrollmentProgressChange(it) } onEnrollmentProgressChange(progress)
}
} }
private val helpMessageObserver: Observer<EnrollmentStatusMessage> = private val helpMessageObserver = Observer { helpMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { helpMessage: EnrollmentStatusMessage? -> Log.d(TAG, "helpMessageObserver($helpMessage)")
helpMessage?.let { onEnrollmentHelp(it) } helpMessage?.let { onEnrollmentHelp(it) }
} }
private val errorMessageObserver: Observer<EnrollmentStatusMessage> =
Observer<EnrollmentStatusMessage> { errorMessage: EnrollmentStatusMessage? -> private val errorMessageObserver = Observer { errorMessage: EnrollmentStatusMessage? ->
Log.d(TAG, "errorMessageObserver($errorMessage)")
errorMessage?.let { onEnrollmentError(it) } errorMessage?.let { onEnrollmentError(it) }
} }
private val canceledSignalObserver = Observer { canceledSignal: Any? ->
Log.d(TAG, "canceledSignalObserver($canceledSignal)")
canceledSignal?.let { onEnrollmentCanceled(it) }
}
private val acquireObserver = private val acquireObserver =
Observer { isAcquiredGood: Boolean? -> isAcquiredGood?.let { onAcquired(it) } } Observer { isAcquiredGood: Boolean? -> isAcquiredGood?.let { onAcquired(it) } }
@@ -144,7 +162,7 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
isEnabled = false isEnabled = false
enrollingViewModel.setOnBackPressed() enrollingViewModel.setOnBackPressed()
cancelEnrollment() cancelEnrollment(true)
} }
} }
@@ -156,6 +174,7 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
_enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java] _enrollingViewModel = provider[FingerprintEnrollEnrollingViewModel::class.java]
_rotationViewModel = provider[DeviceRotationViewModel::class.java] _rotationViewModel = provider[DeviceRotationViewModel::class.java]
_progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java] _progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java]
_errorDialogViewModel = provider[FingerprintEnrollErrorDialogViewModel::class.java]
} }
super.onAttach(context) super.onAttach(context)
requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback) requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback)
@@ -172,7 +191,7 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
): View = (inflater.inflate( ): View = (inflater.inflate(
R.layout.udfps_enroll_enrolling_v2, container, false R.layout.udfps_enroll_enrolling_v2, container, false
) as RelativeLayout).also { ) as RelativeLayout).also {
enrollingUdfpsView = it enrollingView = it
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -181,25 +200,80 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
updateIllustrationLottie(rotation) updateIllustrationLottie(rotation)
requireActivity().bindFingerprintEnrollEnrollingUdfpsView( requireActivity().bindFingerprintEnrollEnrollingUdfpsView(
view = enrollingUdfpsView!!, view = enrollingView!!,
sensorProperties = enrollingViewModel.firstFingerprintSensorPropertiesInternal!!, sensorProperties = enrollingViewModel.firstFingerprintSensorPropertiesInternal!!,
rotation = rotation, rotation = rotation,
onSkipClickListener = onSkipClickListener, onSkipClickListener = onSkipClickListener,
) )
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.triggerRetryFlow.collect { retryEnrollment() }
}
}
} }
private fun retryEnrollment() {
reattachUdfpsEnrollView()
startEnrollment()
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
progressViewModel.helpMessageLiveData.value.let {
if (it != null) {
onEnrollmentHelp(it)
} else {
updateTitleAndDescription()
}
}
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val isEnrolling = progressViewModel.isEnrolling
val isErrorDialogShown = errorDialogViewModel.isDialogShown
Log.d(TAG, "onStart(), isEnrolling:$isEnrolling, isErrorDialog:$isErrorDialogShown")
if (!isErrorDialogShown) {
startEnrollment() startEnrollment()
}
updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!) updateProgress(false /* animate */, progressViewModel.progressLiveData.value!!)
val msg: EnrollmentStatusMessage? = progressViewModel.helpMessageLiveData.value progressViewModel.helpMessageLiveData.value.let {
if (msg != null) { if (it != null) {
onEnrollmentHelp(msg) onEnrollmentHelp(it)
} else { } else {
updateTitleAndDescription() updateTitleAndDescription()
} }
} }
}
private fun reattachUdfpsEnrollView() {
enrollingView!!.let {
val newUdfpsView = LayoutInflater.from(requireActivity()).inflate(
R.layout.udfps_enroll_enrolling_v2_udfps_view,
null
)
val index = it.indexOfChild(udfpsEnrollView)
val lp = udfpsEnrollView.layoutParams
it.removeView(udfpsEnrollView)
it.addView(newUdfpsView, index, lp)
udfpsEnrollView.setSensorProperties(
enrollingViewModel.firstFingerprintSensorPropertiesInternal
)
}
// Clear lottie status
haveShownTipLottie = false
haveShownLeftEdgeLottie = false
haveShownRightEdgeLottie = false
haveShownCenterLottie = false
haveShownGuideLottie = false
illustrationLottie?.let {
it.contentDescription = ""
it.visibility = View.GONE
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
@@ -213,18 +287,17 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
override fun onStop() { override fun onStop() {
removeEnrollmentObservers() removeEnrollmentObservers()
if (!activity!!.isChangingConfigurations && progressViewModel.isEnrolling) { val isEnrolling = progressViewModel.isEnrolling
progressViewModel.cancelEnrollment() val isConfigChange = requireActivity().isChangingConfigurations
Log.d(TAG, "onStop(), enrolling:$isEnrolling isConfigChange:$isConfigChange")
if (isEnrolling && !isConfigChange) {
cancelEnrollment(false)
} }
super.onStop() super.onStop()
} }
private fun removeEnrollmentObservers() { private fun removeEnrollmentObservers() {
preRemoveEnrollmentObservers()
progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver) progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver)
}
private fun preRemoveEnrollmentObservers() {
progressViewModel.progressLiveData.removeObserver(progressObserver) progressViewModel.progressLiveData.removeObserver(progressObserver)
progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver) progressViewModel.helpMessageLiveData.removeObserver(helpMessageObserver)
progressViewModel.acquireLiveData.removeObserver(acquireObserver) progressViewModel.acquireLiveData.removeObserver(acquireObserver)
@@ -232,16 +305,29 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
progressViewModel.pointerUpLiveData.removeObserver(pointerUpObserver) progressViewModel.pointerUpLiveData.removeObserver(pointerUpObserver)
} }
private fun cancelEnrollment() { private fun cancelEnrollment(waitForLastCancelErrMsg: Boolean) {
preRemoveEnrollmentObservers() if (!progressViewModel.isEnrolling) {
progressViewModel.cancelEnrollment() Log.d(TAG, "cancelEnrollment(), failed because isEnrolling is false")
return
}
removeEnrollmentObservers()
if (waitForLastCancelErrMsg) {
progressViewModel.canceledSignalLiveData.observe(this, canceledSignalObserver)
} else {
enrollingCancelSignal = null
}
val cancelResult: Boolean = progressViewModel.cancelEnrollment()
if (!cancelResult) {
Log.e(TAG, "cancelEnrollment(), failed to cancel enrollment")
}
} }
private fun startEnrollment() { private fun startEnrollment() {
val startResult: Boolean = enrollingCancelSignal = progressViewModel.startEnrollment(ENROLL_ENROLL)
progressViewModel.startEnrollment(ENROLL_ENROLL) if (enrollingCancelSignal == null) {
if (!startResult) {
Log.e(TAG, "startEnrollment(), failed") Log.e(TAG, "startEnrollment(), failed")
} else {
Log.d(TAG, "startEnrollment(), success")
} }
progressViewModel.progressLiveData.observe(this, progressObserver) progressViewModel.progressLiveData.observe(this, progressObserver)
progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver) progressViewModel.helpMessageLiveData.observe(this, helpMessageObserver)
@@ -256,19 +342,26 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
Log.d(TAG, "Enrollment not started yet") Log.d(TAG, "Enrollment not started yet")
return return
} }
val progress = getProgress(enrollmentProgress) val progress = getProgress(enrollmentProgress)
if (progressViewModel.progressLiveData.value!!.steps != -1) { Log.d(TAG, "updateProgress($animate, $enrollmentProgress), progress:$progress")
if (enrollmentProgress.steps != -1) {
udfpsEnrollView.onEnrollmentProgress( udfpsEnrollView.onEnrollmentProgress(
enrollmentProgress.remaining, enrollmentProgress.remaining,
enrollmentProgress.steps enrollmentProgress.steps
) )
} }
if (progress >= PROGRESS_BAR_MAX) {
if (animate) { if (animate) {
animateProgress(progress) // Wait animations to finish, then proceed to next page
} else if (progress >= PROGRESS_BAR_MAX) { activity!!.mainThreadHandler.postDelayed(delayedFinishRunnable, 400L)
} else {
delayedFinishRunnable.run() delayedFinishRunnable.run()
} }
} }
}
private fun getProgress(progress: EnrollmentProgress): Int { private fun getProgress(progress: EnrollmentProgress): Int {
if (progress.steps == -1) { if (progress.steps == -1) {
@@ -278,15 +371,8 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
return PROGRESS_BAR_MAX * displayProgress / (progress.steps + 1) return PROGRESS_BAR_MAX * displayProgress / (progress.steps + 1)
} }
private fun animateProgress(progress: Int) {
// UDFPS animations are owned by SystemUI
if (progress >= PROGRESS_BAR_MAX) {
// Wait for any animations in SysUI to finish, then proceed to next page
activity!!.mainThreadHandler.postDelayed(delayedFinishRunnable, 400L)
}
}
private fun updateTitleAndDescription() { private fun updateTitleAndDescription() {
Log.d(TAG, "updateTitleAndDescription($currentStage)")
when (currentStage) { when (currentStage) {
STAGE_CENTER -> { STAGE_CENTER -> {
titleText.setText(R.string.security_settings_fingerprint_enroll_repeat_title) titleText.setText(R.string.security_settings_fingerprint_enroll_repeat_title)
@@ -386,7 +472,7 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
illustrationLottie = null illustrationLottie = null
} else if (shouldShowLottie) { } else if (shouldShowLottie) {
illustrationLottie = illustrationLottie =
enrollingUdfpsView!!.findViewById<LottieAnimationView>(R.id.illustration_lottie) enrollingView!!.findViewById(R.id.illustration_lottie)
} }
} }
@@ -468,6 +554,7 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
} }
private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) { private fun onEnrollmentHelp(helpMessage: EnrollmentStatusMessage) {
Log.d(TAG, "onEnrollmentHelp($helpMessage)")
val helpStr: CharSequence = helpMessage.str val helpStr: CharSequence = helpMessage.str
if (helpStr.isNotEmpty()) { if (helpStr.isNotEmpty()) {
showError(helpStr) showError(helpStr)
@@ -476,25 +563,26 @@ class FingerprintEnrollEnrollingUdfpsFragment : Fragment() {
} }
private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) { private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) {
removeEnrollmentObservers() cancelEnrollment(true)
if (enrollingViewModel.onBackPressed lifecycleScope.launch {
&& errorMessage.msgId == FINGERPRINT_ERROR_CANCELED Log.d(TAG, "newDialog $errorMessage")
) { errorDialogViewModel.newDialog(errorMessage.msgId)
}
}
private fun onEnrollmentCanceled(canceledSignal: Any) {
Log.d(
TAG,
"onEnrollmentCanceled enrolling:$enrollingCancelSignal, canceled:$canceledSignal"
)
if (enrollingCancelSignal === canceledSignal) {
progressViewModel.canceledSignalLiveData.removeObserver(canceledSignalObserver)
progressViewModel.clearProgressLiveData()
if (enrollingViewModel.onBackPressed) {
enrollingViewModel.onCancelledDueToOnBackPressed() enrollingViewModel.onCancelledDueToOnBackPressed()
} else if (enrollingViewModel.onSkipPressed } else if (enrollingViewModel.onSkipPressed) {
&& errorMessage.msgId == FINGERPRINT_ERROR_CANCELED
) {
enrollingViewModel.onCancelledDueToOnSkipPressed() enrollingViewModel.onCancelledDueToOnSkipPressed()
} else { }
val errMsgId: Int = errorMessage.msgId
enrollingViewModel.showErrorDialog(
FingerprintEnrollEnrollingViewModel.ErrorDialogData(
getString(FingerprintErrorDialog.getErrorMessage(errMsgId)),
getString(FingerprintErrorDialog.getErrorTitle(errMsgId)),
errMsgId
)
)
progressViewModel.cancelEnrollment()
} }
} }

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2023 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.biometrics2.ui.view
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS
import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog.getErrorMessage
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog.getErrorTitle
import com.android.settings.biometrics.fingerprint.FingerprintErrorDialog.getSetupErrorMessage
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import kotlinx.coroutines.launch
/**
* Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment.
*/
class FingerprintEnrollErrorDialog : DialogFragment() {
private val viewModel: FingerprintEnrollErrorDialogViewModel?
get() = activity?.let {
ViewModelProvider(it)[FingerprintEnrollErrorDialogViewModel::class.java]
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val errorMsgId: Int = requireArguments().getInt(KEY_ERROR_MSG_ID)
val okButtonSetResultAction =
if (errorMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT)
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
else
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
return requireActivity().bindFingerprintEnrollEnrollingErrorDialog(
errorMsgId = errorMsgId,
isSuw = viewModel!!.isSuw,
tryAgainButtonClickListener = { dialog: DialogInterface?, _: Int ->
activity?.lifecycleScope?.launch {
Log.d(TAG, "tryAgain flow")
viewModel?.triggerRetry()
dialog?.dismiss()
}
},
okButtonClickListener = { dialog: DialogInterface?, _: Int ->
activity?.lifecycleScope?.launch {
Log.d(TAG, "ok flow as $okButtonSetResultAction")
viewModel?.setResultAndFinish(okButtonSetResultAction)
dialog?.dismiss()
}
}
)
}
companion object {
private const val TAG = "FingerprintEnrollErrorDialog"
private const val KEY_ERROR_MSG_ID = "error_msg_id"
fun newInstance(errorMsgId: Int): FingerprintEnrollErrorDialog {
val dialog = FingerprintEnrollErrorDialog()
val args = Bundle()
args.putInt(KEY_ERROR_MSG_ID, errorMsgId)
dialog.arguments = args
return dialog
}
}
}
fun Context.bindFingerprintEnrollEnrollingErrorDialog(
errorMsgId: Int,
isSuw: Boolean,
tryAgainButtonClickListener: DialogInterface.OnClickListener,
okButtonClickListener: DialogInterface.OnClickListener
): AlertDialog = AlertDialog.Builder(this)
.setTitle(getString(getErrorTitle(errorMsgId)))
.setMessage(
getString(
if (isSuw)
getSetupErrorMessage(errorMsgId)
else
getErrorMessage(errorMsgId)
)
)
.setCancelable(false).apply {
if (errorMsgId == FINGERPRINT_ERROR_UNABLE_TO_PROCESS) {
setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_try_again,
tryAgainButtonClickListener
)
setNegativeButton(
R.string.security_settings_fingerprint_enroll_dialog_ok,
okButtonClickListener
)
} else {
setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_ok,
okButtonClickListener
)
}
}
.create()
.apply { setCanceledOnTouchOutside(false) }

View File

@@ -16,7 +16,7 @@
package com.android.settings.biometrics2.ui.view package com.android.settings.biometrics2.ui.view
import android.content.Context import android.content.Context
import android.hardware.fingerprint.FingerprintManager import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR import android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@@ -26,19 +26,25 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation import com.android.settings.biometrics.fingerprint.FingerprintFindSensorAnimation
import com.android.settings.biometrics2.ui.model.EnrollmentProgress import com.android.settings.biometrics2.ui.model.EnrollmentProgress
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.launch
/** /**
* Fragment explaining the side fingerprint sensor location for fingerprint enrollment. * Fragment explaining the side fingerprint sensor location for fingerprint enrollment.
@@ -68,6 +74,10 @@ class FingerprintEnrollFindRfpsFragment : Fragment() {
private val rotationViewModel: DeviceRotationViewModel private val rotationViewModel: DeviceRotationViewModel
get() = _rotationViewModel!! get() = _rotationViewModel!!
private var _errorDialogViewModel: FingerprintEnrollErrorDialogViewModel? = null
private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel
get() = _errorDialogViewModel!!
private var findRfpsView: GlifLayout? = null private var findRfpsView: GlifLayout? = null
private val onSkipClickListener = private val onSkipClickListener =
@@ -75,32 +85,24 @@ class FingerprintEnrollFindRfpsFragment : Fragment() {
private var animation: FingerprintFindSensorAnimation? = null private var animation: FingerprintFindSensorAnimation? = null
private var enrollingCancelSignal: Any? = null
@Surface.Rotation @Surface.Rotation
private var lastRotation = -1 private var lastRotation = -1
private val rotationObserver = Observer { rotation: Int? -> private val progressObserver = Observer { progress: EnrollmentProgress? ->
if (DEBUG) {
Log.d(TAG, "rotationObserver $rotation")
}
rotation?.let { onRotationChanged(it) }
}
private val progressObserver: Observer<EnrollmentProgress> =
Observer<EnrollmentProgress> { progress: EnrollmentProgress? ->
if (DEBUG) {
Log.d(TAG, "progressObserver($progress)")
}
if (progress != null && !progress.isInitialStep) { if (progress != null && !progress.isInitialStep) {
stopLookingForFingerprint(true) cancelEnrollment(true)
} }
} }
private val lastCancelMessageObserver: Observer<EnrollmentStatusMessage> = private val errorMessageObserver = Observer { errorMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { errorMessage: EnrollmentStatusMessage? -> Log.d(TAG, "errorMessageObserver($errorMessage)")
if (DEBUG) { errorMessage?.let { onEnrollmentError(it) }
Log.d(TAG, "lastCancelMessageObserver($errorMessage)")
} }
errorMessage?.let { onLastCancelMessage(it) }
private val canceledSignalObserver = Observer { canceledSignal: Any? ->
canceledSignal?.let { onEnrollmentCanceled(it) }
} }
override fun onCreateView( override fun onCreateView(
@@ -129,29 +131,41 @@ class FingerprintEnrollFindRfpsFragment : Fragment() {
view = findRfpsView!!, view = findRfpsView!!,
onSkipClickListener = onSkipClickListener onSkipClickListener = onSkipClickListener
) )
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.triggerRetryFlow.collect { retryLookingForFingerprint() }
}
}
}
private fun retryLookingForFingerprint() {
startEnrollment()
animation?.let {
Log.d(TAG, "retry, start animation")
it.startAnimation()
}
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
if (DEBUG) { val isErrorDialogShown = errorDialogViewModel.isDialogShown
Log.d( Log.d(TAG, "onStart(), isEnrolling:${progressViewModel.isEnrolling}"
TAG, + ", isErrorDialog:$isErrorDialogShown")
"onStart(), start looking for fingerprint, animation exist:${animation != null}" if (!isErrorDialogShown) {
) startEnrollment()
} }
startLookingForFingerprint()
} }
override fun onResume() { override fun onResume() {
val rotationLiveData: LiveData<Int> = rotationViewModel.liveData val rotationLiveData: LiveData<Int> = rotationViewModel.liveData
lastRotation = rotationLiveData.value!! lastRotation = rotationLiveData.value!!
rotationLiveData.observe(this, rotationObserver) if (!errorDialogViewModel.isDialogShown) {
animation?.let { animation?.let {
if (DEBUG) {
Log.d(TAG, "onResume(), start animation") Log.d(TAG, "onResume(), start animation")
}
it.startAnimation() it.startAnimation()
} }
}
super.onResume() super.onResume()
} }
@@ -167,72 +181,68 @@ class FingerprintEnrollFindRfpsFragment : Fragment() {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
val isEnrolling: Boolean = progressViewModel.isEnrolling removeEnrollmentObservers()
if (DEBUG) { val isEnrolling = progressViewModel.isEnrolling
Log.d( val isConfigChange = requireActivity().isChangingConfigurations
TAG, Log.d(TAG, "onStop(), enrolling:$isEnrolling isConfigChange:$isConfigChange")
"onStop(), current enrolling: ${isEnrolling}, animation exist:${animation != null}" if (isEnrolling && !isConfigChange) {
) cancelEnrollment(false)
}
if (isEnrolling) {
stopLookingForFingerprint(false)
} }
} }
private fun startLookingForFingerprint() { private fun removeEnrollmentObservers() {
if (progressViewModel.isEnrolling) { progressViewModel.progressLiveData.removeObserver(progressObserver)
Log.d( progressViewModel.helpMessageLiveData.removeObserver(errorMessageObserver)
TAG,
"startLookingForFingerprint(), failed because isEnrolling is true before starting"
)
return
} }
val startResult: Boolean = progressViewModel.startEnrollment(ENROLL_FIND_SENSOR)
if (!startResult) { private fun startEnrollment() {
Log.e(TAG, "startLookingForFingerprint(), failed to start enrollment") enrollingCancelSignal = progressViewModel.startEnrollment(ENROLL_FIND_SENSOR)
if (enrollingCancelSignal == null) {
Log.e(TAG, "startEnrollment(), failed to start enrollment")
} else {
Log.d(TAG, "startEnrollment(), success")
} }
progressViewModel.progressLiveData.observe(this, progressObserver) progressViewModel.progressLiveData.observe(this, progressObserver)
progressViewModel.errorMessageLiveData.observe(this, errorMessageObserver)
} }
private fun stopLookingForFingerprint(waitForLastCancelErrMsg: Boolean) { private fun cancelEnrollment(waitForLastCancelErrMsg: Boolean) {
if (!progressViewModel.isEnrolling) { if (!progressViewModel.isEnrolling) {
Log.d( Log.d(TAG, "cancelEnrollment(), failed because isEnrolling is false")
TAG,
"stopLookingForFingerprint(), failed because isEnrolling is false before stopping"
)
return return
} }
removeEnrollmentObservers()
if (waitForLastCancelErrMsg) { if (waitForLastCancelErrMsg) {
progressViewModel.clearErrorMessageLiveData() // Prevent got previous error message progressViewModel.canceledSignalLiveData.observe(this, canceledSignalObserver)
progressViewModel.errorMessageLiveData.observe(this, lastCancelMessageObserver) } else {
enrollingCancelSignal = null
} }
progressViewModel.progressLiveData.removeObserver(progressObserver)
val cancelResult: Boolean = progressViewModel.cancelEnrollment() val cancelResult: Boolean = progressViewModel.cancelEnrollment()
if (!cancelResult) { if (!cancelResult) {
Log.e(TAG, "stopLookingForFingerprint(), failed to cancel enrollment") Log.e(TAG, "cancelEnrollment(), failed to cancel enrollment")
} }
} }
private fun onRotationChanged(@Surface.Rotation newRotation: Int) { private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) {
if (DEBUG) { cancelEnrollment(false)
Log.d(TAG, "onRotationChanged() from $lastRotation to $newRotation") lifecycleScope.launch {
} Log.d(TAG, "newDialogFlow as $errorMessage")
if (newRotation % 2 != lastRotation % 2) { errorDialogViewModel.newDialog(errorMessage.msgId)
// Fragment is going to be recreated, just stopLookingForFingerprint() here.
stopLookingForFingerprint(true)
} }
} }
private fun onLastCancelMessage(errorMessage: EnrollmentStatusMessage) { private fun onEnrollmentCanceled(canceledSignal: Any) {
if (errorMessage.msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) { Log.d(
TAG,
"onEnrollmentCanceled enrolling:$enrollingCancelSignal, canceled:$canceledSignal"
)
if (enrollingCancelSignal === canceledSignal) {
val progress: EnrollmentProgress? = progressViewModel.progressLiveData.value val progress: EnrollmentProgress? = progressViewModel.progressLiveData.value
progressViewModel.canceledSignalLiveData.removeObserver(canceledSignalObserver)
progressViewModel.clearProgressLiveData() progressViewModel.clearProgressLiveData()
progressViewModel.errorMessageLiveData.removeObserver(lastCancelMessageObserver)
if (progress != null && !progress.isInitialStep) { if (progress != null && !progress.isInitialStep) {
viewModel.onStartButtonClick() viewModel.onStartButtonClick()
} }
} else {
Log.e(TAG, "errorMessageObserver($errorMessage)")
} }
} }
@@ -251,6 +261,7 @@ class FingerprintEnrollFindRfpsFragment : Fragment() {
_viewModel = provider[FingerprintEnrollFindSensorViewModel::class.java] _viewModel = provider[FingerprintEnrollFindSensorViewModel::class.java]
_progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java] _progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java]
_rotationViewModel = provider[DeviceRotationViewModel::class.java] _rotationViewModel = provider[DeviceRotationViewModel::class.java]
_errorDialogViewModel = provider[FingerprintEnrollErrorDialogViewModel::class.java]
} }
super.onAttach(context) super.onAttach(context)
} }

View File

@@ -16,7 +16,8 @@
package com.android.settings.biometrics2.ui.view package com.android.settings.biometrics2.ui.view
import android.content.Context import android.content.Context
import android.hardware.fingerprint.FingerprintManager import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -26,21 +27,27 @@ import android.view.ViewGroup
import androidx.annotation.RawRes import androidx.annotation.RawRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieAnimationView
import com.android.settings.R import com.android.settings.R
import com.android.settings.biometrics2.ui.model.EnrollmentProgress import com.android.settings.biometrics2.ui.model.EnrollmentProgress
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel import com.android.settings.biometrics2.ui.viewmodel.DeviceRotationViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.android.settingslib.widget.LottieColorUtils import com.android.settingslib.widget.LottieColorUtils
import com.google.android.setupcompat.template.FooterBarMixin import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton import com.google.android.setupcompat.template.FooterButton
import com.google.android.setupdesign.GlifLayout import com.google.android.setupdesign.GlifLayout
import kotlinx.coroutines.launch
/** /**
* Fragment explaining the side fingerprint sensor location for fingerprint enrollment. * Fragment explaining the side fingerprint sensor location for fingerprint enrollment.
@@ -75,40 +82,40 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
private val foldedViewModel: DeviceFoldedViewModel private val foldedViewModel: DeviceFoldedViewModel
get() = _foldedViewModel!! get() = _foldedViewModel!!
private var _errorDialogViewModel: FingerprintEnrollErrorDialogViewModel? = null
private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel
get() = _errorDialogViewModel!!
private var findSfpsView: GlifLayout? = null private var findSfpsView: GlifLayout? = null
private val onSkipClickListener = private val onSkipClickListener =
View.OnClickListener { _: View? -> viewModel.onSkipButtonClick() } View.OnClickListener { _: View? -> viewModel.onSkipButtonClick() }
private val illustrationLottie: LottieAnimationView private val illustrationLottie: LottieAnimationView
get() = findSfpsView!!.findViewById<LottieAnimationView>(R.id.illustration_lottie)!! get() = findSfpsView!!.findViewById(R.id.illustration_lottie)!!
private var enrollingCancelSignal: Any? = null
@Surface.Rotation @Surface.Rotation
private var animationRotation = -1 private var animationRotation = -1
private val rotationObserver = Observer { rotation: Int? -> private val rotationObserver = Observer { rotation: Int? ->
if (DEBUG) {
Log.d(TAG, "rotationObserver $rotation")
}
rotation?.let { onRotationChanged(it) } rotation?.let { onRotationChanged(it) }
} }
private val progressObserver: Observer<EnrollmentProgress> = private val progressObserver = Observer { progress: EnrollmentProgress? ->
Observer<EnrollmentProgress> { progress: EnrollmentProgress? ->
if (DEBUG) {
Log.d(TAG, "progressObserver($progress)")
}
if (progress != null && !progress.isInitialStep) { if (progress != null && !progress.isInitialStep) {
stopLookingForFingerprint(true) cancelEnrollment(true)
} }
} }
private val lastCancelMessageObserver: Observer<EnrollmentStatusMessage> = private val errorMessageObserver = Observer{ errorMessage: EnrollmentStatusMessage? ->
Observer<EnrollmentStatusMessage> { errorMessage: EnrollmentStatusMessage? -> Log.d(TAG, "errorMessageObserver($errorMessage)")
if (DEBUG) { errorMessage?.let { onEnrollmentError(it) }
Log.d(TAG, "lastCancelMessageObserver($errorMessage)")
} }
errorMessage?.let { onLastCancelMessage(it) }
private val canceledSignalObserver = Observer { canceledSignal: Any? ->
canceledSignal?.let { onEnrollmentCanceled(it) }
} }
override fun onCreateView( override fun onCreateView(
@@ -128,16 +135,21 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
view = findSfpsView!!, view = findSfpsView!!,
onSkipClickListener = onSkipClickListener onSkipClickListener = onSkipClickListener
) )
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.triggerRetryFlow.collect { startEnrollment() }
}
}
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val isEnrolling: Boolean = progressViewModel.isEnrolling val isErrorDialogShown = errorDialogViewModel.isDialogShown
if (DEBUG) { Log.d(TAG, "onStart(), isEnrolling:${progressViewModel.isEnrolling}"
Log.d(TAG, "onStart(), isEnrolling:$isEnrolling") + ", isErrorDialog:$isErrorDialogShown")
} if (!isErrorDialogShown) {
if (!isEnrolling) { startEnrollment()
startLookingForFingerprint()
} }
} }
@@ -155,51 +167,44 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
val isEnrolling: Boolean = progressViewModel.isEnrolling val isEnrolling = progressViewModel.isEnrolling
if (DEBUG) { val isConfigChange = requireActivity().isChangingConfigurations
Log.d(TAG, "onStop(), isEnrolling:$isEnrolling") Log.d(TAG, "onStop(), enrolling:$isEnrolling isConfigChange:$isConfigChange")
} if (isEnrolling && !isConfigChange) {
if (isEnrolling) { cancelEnrollment(false)
stopLookingForFingerprint(false)
} }
} }
private fun startLookingForFingerprint() { private fun removeEnrollmentObservers() {
if (progressViewModel.isEnrolling) { progressViewModel.errorMessageLiveData.removeObserver(errorMessageObserver)
Log.d(
TAG,
"startLookingForFingerprint(), failed because isEnrolling is true before starting"
)
return
}
progressViewModel.clearProgressLiveData()
progressViewModel.progressLiveData.observe(this, progressObserver)
val startResult: Boolean =
progressViewModel.startEnrollment(FingerprintManager.ENROLL_FIND_SENSOR)
if (!startResult) {
Log.e(TAG, "startLookingForFingerprint(), failed to start enrollment")
}
}
private fun stopLookingForFingerprint(waitForLastCancelErrMsg: Boolean) {
if (!progressViewModel.isEnrolling) {
Log.d(
TAG, "stopLookingForFingerprint(), failed because isEnrolling is false before"
+ " stopping"
)
return
}
if (waitForLastCancelErrMsg) {
progressViewModel.clearErrorMessageLiveData() // Prevent got previous error message
progressViewModel.errorMessageLiveData.observe(
this,
lastCancelMessageObserver
)
}
progressViewModel.progressLiveData.removeObserver(progressObserver) progressViewModel.progressLiveData.removeObserver(progressObserver)
}
private fun startEnrollment() {
enrollingCancelSignal = progressViewModel.startEnrollment(ENROLL_FIND_SENSOR)
if (enrollingCancelSignal == null) {
Log.e(TAG, "startEnrollment(), failed to start enrollment")
} else {
Log.d(TAG, "startEnrollment(), success")
}
progressViewModel.progressLiveData.observe(this, progressObserver)
progressViewModel.errorMessageLiveData.observe(this, errorMessageObserver)
}
private fun cancelEnrollment(waitForLastCancelErrMsg: Boolean) {
if (!progressViewModel.isEnrolling) {
Log.d(TAG, "cancelEnrollment(), failed because isEnrolling is false")
return
}
removeEnrollmentObservers()
if (waitForLastCancelErrMsg) {
progressViewModel.canceledSignalLiveData.observe(this, canceledSignalObserver)
} else {
enrollingCancelSignal = null
}
val cancelResult: Boolean = progressViewModel.cancelEnrollment() val cancelResult: Boolean = progressViewModel.cancelEnrollment()
if (!cancelResult) { if (!cancelResult) {
Log.e(TAG, "stopLookingForFingerprint(), failed to cancel enrollment") Log.e(TAG, "cancelEnrollment(), failed to cancel enrollment")
} }
} }
@@ -210,34 +215,39 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
if ((newRotation + 2) % 4 == animationRotation) { if ((newRotation + 2) % 4 == animationRotation) {
// Fragment not changed, we just need to play correct rotation animation // Fragment not changed, we just need to play correct rotation animation
playLottieAnimation(newRotation) playLottieAnimation(newRotation)
} else if (newRotation % 2 != animationRotation % 2) {
// Fragment is going to be recreated, just stopLookingForFingerprint() here.
stopLookingForFingerprint(true)
} }
} }
private fun onLastCancelMessage(errorMessage: EnrollmentStatusMessage) { private fun onEnrollmentError(errorMessage: EnrollmentStatusMessage) {
if (errorMessage.msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) { progressViewModel.cancelEnrollment()
lifecycleScope.launch {
Log.d(TAG, "newDialogFlow as $errorMessage")
errorDialogViewModel.newDialog(errorMessage.msgId)
}
}
private fun onEnrollmentCanceled(canceledSignal: Any) {
Log.d(
TAG,
"onEnrollmentCanceled enrolling:$enrollingCancelSignal, canceled:$canceledSignal"
)
if (enrollingCancelSignal === canceledSignal) {
val progress: EnrollmentProgress? = progressViewModel.progressLiveData.value val progress: EnrollmentProgress? = progressViewModel.progressLiveData.value
progressViewModel.canceledSignalLiveData.removeObserver(canceledSignalObserver)
progressViewModel.clearProgressLiveData() progressViewModel.clearProgressLiveData()
progressViewModel.errorMessageLiveData.removeObserver(lastCancelMessageObserver)
if (progress != null && !progress.isInitialStep) { if (progress != null && !progress.isInitialStep) {
viewModel.onStartButtonClick() viewModel.onStartButtonClick()
} }
} else {
Log.e(TAG, "errorMessageObserver($errorMessage)")
} }
} }
private fun playLottieAnimation(@Surface.Rotation rotation: Int) { private fun playLottieAnimation(@Surface.Rotation rotation: Int) {
@RawRes val animationRawRes = getSfpsLottieAnimationRawRes(rotation) @RawRes val animationRawRes = getSfpsLottieAnimationRawRes(rotation)
if (DEBUG) {
Log.d( Log.d(
TAG, TAG,
"play lottie animation $animationRawRes, previous rotation:$animationRotation" "play lottie animation $animationRawRes, previous rotation:$animationRotation"
+ ", new rotation:" + rotation + ", new rotation:" + rotation
) )
}
animationRotation = rotation animationRotation = rotation
illustrationLottie.setAnimation(animationRawRes) illustrationLottie.setAnimation(animationRawRes)
LottieColorUtils.applyDynamicColors(activity, illustrationLottie) LottieColorUtils.applyDynamicColors(activity, illustrationLottie)
@@ -247,7 +257,7 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
@RawRes @RawRes
private fun getSfpsLottieAnimationRawRes(@Surface.Rotation rotation: Int): Int { private fun getSfpsLottieAnimationRawRes(@Surface.Rotation rotation: Int): Int {
val isFolded = java.lang.Boolean.FALSE != foldedViewModel.getLiveData().getValue() val isFolded = java.lang.Boolean.FALSE != foldedViewModel.liveData.value
return when (rotation) { return when (rotation) {
Surface.ROTATION_90 -> Surface.ROTATION_90 ->
if (isFolded) if (isFolded)
@@ -278,6 +288,7 @@ class FingerprintEnrollFindSfpsFragment : Fragment() {
_progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java] _progressViewModel = provider[FingerprintEnrollProgressViewModel::class.java]
_rotationViewModel = provider[DeviceRotationViewModel::class.java] _rotationViewModel = provider[DeviceRotationViewModel::class.java]
_foldedViewModel = provider[DeviceFoldedViewModel::class.java] _foldedViewModel = provider[DeviceFoldedViewModel::class.java]
_errorDialogViewModel = provider[FingerprintEnrollErrorDialogViewModel::class.java]
} }
super.onAttach(context) super.onAttach(context)
} }

View File

@@ -32,8 +32,11 @@ import androidx.annotation.ColorInt
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras import androidx.lifecycle.viewmodel.MutableCreationExtras
import com.android.settings.R import com.android.settings.R
@@ -53,15 +56,12 @@ import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CRE
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.FingerprintChallengeGenerator
import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel import com.android.settings.biometrics2.ui.viewmodel.DeviceFoldedViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintEnrollEnrollingAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollErrorDialogViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP
@@ -78,8 +78,11 @@ import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewM
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroViewModel.FingerprintEnrollIntroAction
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollProgressViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.google.android.setupdesign.util.ThemeHelper import com.google.android.setupdesign.util.ThemeHelper
import kotlinx.coroutines.launch
/** /**
* Fingerprint enrollment activity implementation * Fingerprint enrollment activity implementation
@@ -103,6 +106,30 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
viewModelProvider[AutoCredentialViewModel::class.java] viewModelProvider[AutoCredentialViewModel::class.java]
} }
private val introViewModel: FingerprintEnrollIntroViewModel by lazy {
viewModelProvider[FingerprintEnrollIntroViewModel::class.java]
}
private val findSensorViewModel: FingerprintEnrollFindSensorViewModel by lazy {
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java]
}
private val progressViewModel: FingerprintEnrollProgressViewModel by lazy {
viewModelProvider[FingerprintEnrollProgressViewModel::class.java]
}
private val enrollingViewModel: FingerprintEnrollEnrollingViewModel by lazy {
viewModelProvider[FingerprintEnrollEnrollingViewModel::class.java]
}
private val finishViewModel: FingerprintEnrollFinishViewModel by lazy {
viewModelProvider[FingerprintEnrollFinishViewModel::class.java]
}
private val errorDialogViewModel: FingerprintEnrollErrorDialogViewModel by lazy {
viewModelProvider[FingerprintEnrollErrorDialogViewModel::class.java]
}
private val introActionObserver: Observer<Int> = Observer<Int> { action -> private val introActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "introActionObserver($action)") Log.d(TAG, "introActionObserver($action)")
@@ -124,26 +151,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
action?.let { onEnrollingAction(it) } action?.let { onEnrollingAction(it) }
} }
private val enrollingErrorDialogObserver: Observer<ErrorDialogData> =
Observer<ErrorDialogData> { data ->
if (DEBUG) {
Log.d(TAG, "enrollingErrorDialogObserver($data)")
}
data?.let {
FingerprintEnrollEnrollingErrorDialog().show(
supportFragmentManager,
ENROLLING_ERROR_DIALOG_TAG
)
}
}
private val enrollingErrorDialogActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) {
Log.d(TAG, "enrollingErrorDialogActionObserver($action)")
}
action?.let { onEnrollingErrorDialogAction(it) }
}
private val finishActionObserver: Observer<Int> = Observer<Int> { action -> private val finishActionObserver: Observer<Int> = Observer<Int> { action ->
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "finishActionObserver($action)") Log.d(TAG, "finishActionObserver($action)")
@@ -218,6 +225,33 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
autoCredentialViewModel.generateChallengeFailedLiveData.observe(this) { autoCredentialViewModel.generateChallengeFailedLiveData.observe(this) {
_: Boolean -> onGenerateChallengeFailed() _: Boolean -> onGenerateChallengeFailed()
} }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.newDialogFlow.collect {
Log.d(TAG, "newErrorDialogFlow($it)")
FingerprintEnrollErrorDialog.newInstance(it).show(
supportFragmentManager,
ERROR_DIALOG_TAG
)
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
errorDialogViewModel.setResultFlow.collect {
Log.d(TAG, "errorDialogSetResultFlow($it)")
when (it) {
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
)
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)
)
}
}
}
}
} }
private fun startFragment(fragmentClass: Class<out Fragment>, tag: String) { private fun startFragment(fragmentClass: Class<out Fragment>, tag: String) {
@@ -252,7 +286,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
if (request.isSkipIntro || request.isSkipFindSensor) { if (request.isSkipIntro || request.isSkipFindSensor) {
return return
} }
viewModelProvider[FingerprintEnrollIntroViewModel::class.java].let { introViewModel.let {
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor // recreate, like press 'Agree' then press 'back' in FingerprintEnrollFindSensor
// activity. // activity.
@@ -264,8 +298,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
// We need to make sure token is valid before entering find sensor page // We need to make sure token is valid before entering find sensor page
private fun startFindSensorFragment() { private fun startFindSensorFragment() {
// Always setToken into progressViewModel even it is not necessary action for UDFPS // Always setToken into progressViewModel even it is not necessary action for UDFPS
viewModelProvider[FingerprintEnrollProgressViewModel::class.java] progressViewModel.setToken(autoCredentialViewModel.token)
.setToken(autoCredentialViewModel.token)
attachFindSensorViewModel() attachFindSensorViewModel()
val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) { val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
FingerprintEnrollFindUdfpsFragment::class.java FingerprintEnrollFindUdfpsFragment::class.java
@@ -281,7 +314,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
if (viewModel.request.isSkipFindSensor) { if (viewModel.request.isSkipFindSensor) {
return return
} }
viewModelProvider[FingerprintEnrollFindSensorViewModel::class.java].let { findSensorViewModel.let {
// Clear ActionLiveData in FragmentViewModel to prevent getting previous action during // Clear ActionLiveData in FragmentViewModel to prevent getting previous action during
// recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling // recreate, like press 'Start' then press 'back' in FingerprintEnrollEnrolling
// activity. // activity.
@@ -292,8 +325,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
private fun startEnrollingFragment() { private fun startEnrollingFragment() {
// Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS // Always setToken into progressViewModel even it is not necessary action for SFPS or RFPS
viewModelProvider[FingerprintEnrollProgressViewModel::class.java] progressViewModel.setToken(autoCredentialViewModel.token)
.setToken(autoCredentialViewModel.token)
attachEnrollingViewModel() attachEnrollingViewModel()
val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) { val fragmentClass: Class<out Fragment> = if (viewModel.canAssumeUdfps()) {
FingerprintEnrollEnrollingUdfpsFragment::class.java FingerprintEnrollEnrollingUdfpsFragment::class.java
@@ -306,14 +338,9 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
} }
private fun attachEnrollingViewModel() { private fun attachEnrollingViewModel() {
viewModelProvider[FingerprintEnrollEnrollingViewModel::class.java].let { enrollingViewModel.let {
it.clearActionLiveData() it.clearActionLiveData()
it.actionLiveData.observe(this, enrollingActionObserver) it.actionLiveData.observe(this, enrollingActionObserver)
it.errorDialogLiveData.observe(this, enrollingErrorDialogObserver)
it.errorDialogActionLiveData.observe(
this,
enrollingErrorDialogActionObserver
)
} }
} }
@@ -374,7 +401,7 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
} }
private fun attachFinishViewModel() { private fun attachFinishViewModel() {
viewModelProvider[FingerprintEnrollFinishViewModel::class.java].let { finishViewModel.let {
it.clearActionLiveData() it.clearActionLiveData()
it.actionLiveData.observe(this, finishActionObserver) it.actionLiveData.observe(this, finishActionObserver)
} }
@@ -520,18 +547,6 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
} }
} }
private fun onEnrollingErrorDialogAction(@FingerprintErrorDialogAction action: Int) {
when (action) {
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_FINISHED, null)
)
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT -> onSetActivityResult(
ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null)
)
}
}
private fun onFinishAction(@FingerprintEnrollFinishAction action: Int) { private fun onFinishAction(@FingerprintEnrollFinishAction action: Int) {
when (action) { when (action) {
FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK -> { FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK -> {
@@ -623,12 +638,13 @@ open class FingerprintEnrollmentActivity : FragmentActivity() {
companion object { companion object {
private const val DEBUG = false private const val DEBUG = false
private const val TAG = "FingerprintEnrollmentActivity" private const val TAG = "FingerprintEnrollmentActivity"
protected const val LAUNCH_CONFIRM_LOCK_ACTIVITY = 1
private const val INTRO_TAG = "intro" private const val INTRO_TAG = "intro"
private const val FIND_SENSOR_TAG = "find-sensor" private const val FIND_SENSOR_TAG = "find-sensor"
private const val ENROLLING_TAG = "enrolling" private const val ENROLLING_TAG = "enrolling"
private const val FINISH_TAG = "finish" private const val FINISH_TAG = "finish"
private const val SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog" private const val SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog"
private const val ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog" private const val ERROR_DIALOG_TAG = "error-dialog"
protected const val LAUNCH_CONFIRM_LOCK_ACTIVITY = 1
} }
} }

View File

@@ -59,9 +59,7 @@ public class DeviceRotationViewModel extends AndroidViewModel {
@Override @Override
public void onDisplayChanged(int displayId) { public void onDisplayChanged(int displayId) {
final int rotation = getRotation(); final int rotation = getRotation();
if (DEBUG) {
Log.d(TAG, "onDisplayChanged(" + displayId + "), rotation:" + rotation); Log.d(TAG, "onDisplayChanged(" + displayId + "), rotation:" + rotation);
}
mLiveData.postValue(rotation); mLiveData.postValue(rotation);
} }
}; };
@@ -98,10 +96,11 @@ public class DeviceRotationViewModel extends AndroidViewModel {
* Returns RotationLiveData * Returns RotationLiveData
*/ */
public LiveData<Integer> getLiveData() { public LiveData<Integer> getLiveData() {
if (mLiveData.getValue() == null) { final Integer lastRotation = mLiveData.getValue();
// Init data here because if we set it through getDisplay().getRotation() or through @Surface.Rotation int newRotation = getRotation();
// getDisplay().getDisplayInfo() in constructor(), we always get incorrect value. if (lastRotation == null || lastRotation != newRotation) {
mLiveData.setValue(getRotation()); Log.d(TAG, "getLiveData, update rotation from " + lastRotation + " to " + newRotation);
mLiveData.setValue(newRotation);
} }
return mLiveData; return mLiveData;
} }

View File

@@ -73,29 +73,11 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
@IntDef(prefix = { "FINGERPRINT_ENROLL_ENROLLING_ACTION_" }, value = { @IntDef(prefix = { "FINGERPRINT_ENROLL_ENROLLING_ACTION_" }, value = {
FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE, FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE,
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG, FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG,
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP, FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface FingerprintEnrollEnrollingAction {} public @interface FingerprintEnrollEnrollingAction {}
/**
* Enrolling skipped
*/
public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH = 0;
/**
* Enrolling finished
*/
public static final int FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT = 1;
@IntDef(prefix = { "FINGERPRINT_ERROR_DIALOG_ACTION_" }, value = {
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH,
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
})
@Retention(RetentionPolicy.SOURCE)
public @interface FingerprintErrorDialogAction {}
private final int mUserId; private final int mUserId;
private boolean mOnBackPressed; private boolean mOnBackPressed;
private boolean mOnSkipPressed; private boolean mOnSkipPressed;
@@ -104,11 +86,12 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
private final Vibrator mVibrator; private final Vibrator mVibrator;
private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>(); private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>();
private final MutableLiveData<ErrorDialogData> mErrorDialogLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mErrorDialogActionLiveData = new MutableLiveData<>();
public FingerprintEnrollEnrollingViewModel(@NonNull Application application, public FingerprintEnrollEnrollingViewModel(
int userId, @NonNull FingerprintRepository fingerprintRepository) { @NonNull Application application,
int userId,
@NonNull FingerprintRepository fingerprintRepository
) {
super(application); super(application);
mUserId = userId; mUserId = userId;
mFingerprintRepository = fingerprintRepository; mFingerprintRepository = fingerprintRepository;
@@ -116,21 +99,6 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
mVibrator = application.getSystemService(Vibrator.class); mVibrator = application.getSystemService(Vibrator.class);
} }
/**
* Notifies activity to show error dialog
*/
public void showErrorDialog(@NonNull ErrorDialogData errorDialogData) {
mErrorDialogLiveData.postValue(errorDialogData);
}
public LiveData<ErrorDialogData> getErrorDialogLiveData() {
return mErrorDialogLiveData;
}
public LiveData<Integer> getErrorDialogActionLiveData() {
return mErrorDialogActionLiveData;
}
public LiveData<Integer> getActionLiveData() { public LiveData<Integer> getActionLiveData() {
return mActionLiveData; return mActionLiveData;
} }
@@ -142,16 +110,6 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
mActionLiveData.setValue(null); mActionLiveData.setValue(null);
} }
/**
* Saves new user dialog action to mErrorDialogActionLiveData
*/
public void onErrorDialogAction(@FingerprintErrorDialogAction int action) {
if (DEBUG) {
Log.d(TAG, "onErrorDialogAction(" + action + ")");
}
mErrorDialogActionLiveData.postValue(action);
}
public boolean getOnSkipPressed() { public boolean getOnSkipPressed() {
return mOnSkipPressed; return mOnSkipPressed;
} }
@@ -164,7 +122,7 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
} }
/** /**
* Enrolling is cacelled because user clicks skip * Enrolling is cancelled because user clicks skip
*/ */
public void onCancelledDueToOnSkipPressed() { public void onCancelledDueToOnSkipPressed() {
final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP; final int action = FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
@@ -287,38 +245,4 @@ public class FingerprintEnrollEnrollingViewModel extends AndroidViewModel {
public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() { public FingerprintSensorPropertiesInternal getFirstFingerprintSensorPropertiesInternal() {
return mFingerprintRepository.getFirstFingerprintSensorPropertiesInternal(); return mFingerprintRepository.getFirstFingerprintSensorPropertiesInternal();
} }
/**
* Data for passing to FingerprintEnrollEnrollingErrorDialog
*/
public static class ErrorDialogData {
@NonNull private final CharSequence mErrMsg;
@NonNull private final CharSequence mErrTitle;
@NonNull private final int mErrMsgId;
public ErrorDialogData(@NonNull CharSequence errMsg, @NonNull CharSequence errTitle,
int errMsgId) {
mErrMsg = errMsg;
mErrTitle = errTitle;
mErrMsgId = errMsgId;
}
public CharSequence getErrMsg() {
return mErrMsg;
}
public CharSequence getErrTitle() {
return mErrTitle;
}
public int getErrMsgId() {
return mErrMsgId;
}
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
+ "{id:" + mErrMsgId + "}";
}
}
} }

View File

@@ -0,0 +1,51 @@
package com.android.settings.biometrics2.ui.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import kotlinx.atomicfu.AtomicBoolean
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
class FingerprintEnrollErrorDialogViewModel(
application: Application,
val isSuw: Boolean
): AndroidViewModel(application) {
private val _isDialogShown: AtomicBoolean = atomic(false)
val isDialogShown: Boolean
get() = _isDialogShown.value
private val _newDialogFlow = MutableSharedFlow<Int>()
val newDialogFlow: SharedFlow<Int>
get() = _newDialogFlow.asSharedFlow()
private val _triggerRetryFlow = MutableSharedFlow<Any>()
val triggerRetryFlow: SharedFlow<Any>
get() = _triggerRetryFlow.asSharedFlow()
private val _setResultFlow = MutableSharedFlow<FingerprintErrorDialogSetResultAction>()
val setResultFlow: SharedFlow<FingerprintErrorDialogSetResultAction>
get() = _setResultFlow.asSharedFlow()
suspend fun newDialog(errorMsgId: Int) {
_isDialogShown.compareAndSet(expect = false, update = true)
_newDialogFlow.emit(errorMsgId)
}
suspend fun triggerRetry() {
_isDialogShown.compareAndSet(expect = true, update = false)
_triggerRetryFlow.emit(Any())
}
suspend fun setResultAndFinish(action: FingerprintErrorDialogSetResultAction) {
_isDialogShown.compareAndSet(expect = true, update = false)
_setResultFlow.emit(action)
}
}
enum class FingerprintErrorDialogSetResultAction {
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH,
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.biometrics2.ui.viewmodel; package com.android.settings.biometrics2.ui.viewmodel;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED;
import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING; import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING;
@@ -41,6 +42,8 @@ import com.android.settings.biometrics.fingerprint.MessageDisplayController;
import com.android.settings.biometrics2.ui.model.EnrollmentProgress; import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage; import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
import java.util.LinkedList;
/** /**
* Progress ViewModel handles the state around biometric enrollment. It manages the state of * Progress ViewModel handles the state around biometric enrollment. It manages the state of
* enrollment throughout the activity lifecycle so the app can continue after an event like * enrollment throughout the activity lifecycle so the app can continue after an event like
@@ -57,6 +60,7 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveData<EnrollmentStatusMessage> mErrorMessageLiveData = private final MutableLiveData<EnrollmentStatusMessage> mErrorMessageLiveData =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveData<Object> mCanceledSignalLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> mAcquireLiveData = new MutableLiveData<>(); private final MutableLiveData<Boolean> mAcquireLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mPointerDownLiveData = new MutableLiveData<>(); private final MutableLiveData<Integer> mPointerDownLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mPointerUpLiveData = new MutableLiveData<>(); private final MutableLiveData<Integer> mPointerUpLiveData = new MutableLiveData<>();
@@ -66,6 +70,8 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
private final FingerprintUpdater mFingerprintUpdater; private final FingerprintUpdater mFingerprintUpdater;
@Nullable private CancellationSignal mCancellationSignal = null; @Nullable private CancellationSignal mCancellationSignal = null;
@NonNull private final LinkedList<CancellationSignal> mCancelingSignalQueue =
new LinkedList<>();
private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() { private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() {
@Override @Override
@@ -91,11 +97,14 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
@Override @Override
public void onEnrollmentError(int errMsgId, CharSequence errString) { public void onEnrollmentError(int errMsgId, CharSequence errString) {
if (DEBUG) { Log.d(TAG, "onEnrollmentError(" + errMsgId + ", " + errString
Log.d(TAG, "onEnrollmentError(" + errMsgId + ", " + errString + ")"); + "), cancelingQueueSize:" + mCancelingSignalQueue.size());
} if (FINGERPRINT_ERROR_CANCELED == errMsgId && mCancelingSignalQueue.size() > 0) {
mCanceledSignalLiveData.postValue(mCancelingSignalQueue.poll());
} else {
mErrorMessageLiveData.postValue(new EnrollmentStatusMessage(errMsgId, errString)); mErrorMessageLiveData.postValue(new EnrollmentStatusMessage(errMsgId, errString));
} }
}
@Override @Override
public void onAcquired(boolean isAcquiredGood) { public void onAcquired(boolean isAcquiredGood) {
@@ -152,6 +161,10 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
return mErrorMessageLiveData; return mErrorMessageLiveData;
} }
public LiveData<Object> getCanceledSignalLiveData() {
return mCanceledSignalLiveData;
}
public LiveData<Boolean> getAcquireLiveData() { public LiveData<Boolean> getAcquireLiveData() {
return mAcquireLiveData; return mAcquireLiveData;
} }
@@ -167,14 +180,14 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
/** /**
* Starts enrollment and return latest isEnrolling() result * Starts enrollment and return latest isEnrolling() result
*/ */
public boolean startEnrollment(@EnrollReason int reason) { public Object startEnrollment(@EnrollReason int reason) {
if (mToken == null) { if (mToken == null) {
Log.e(TAG, "Null hardware auth token for enroll"); Log.e(TAG, "Null hardware auth token for enroll");
return false; return null;
} }
if (mCancellationSignal != null) { if (mCancellationSignal != null) {
Log.w(TAG, "Enrolling has started, shall not start again"); Log.w(TAG, "Enrolling is running, shall not start again");
return true; return mCancellationSignal;
} }
if (DEBUG) { if (DEBUG) {
Log.e(TAG, "startEnrollment(" + reason + ")"); Log.e(TAG, "startEnrollment(" + reason + ")");
@@ -204,7 +217,7 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback, mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback,
reason); reason);
} }
return true; return mCancellationSignal;
} }
/** /**
@@ -212,13 +225,17 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel {
*/ */
public boolean cancelEnrollment() { public boolean cancelEnrollment() {
final CancellationSignal cancellationSignal = mCancellationSignal; final CancellationSignal cancellationSignal = mCancellationSignal;
mCancellationSignal = null;
if (cancellationSignal == null) { if (cancellationSignal == null) {
Log.e(TAG, "Fail to cancel enrollment, has cancelled or not start"); Log.e(TAG, "Fail to cancel enrollment, has cancelled or not start");
return false; return false;
} else {
Log.d(TAG, "enrollment cancelled");
} }
mCancelingSignalQueue.add(cancellationSignal);
mCancellationSignal = null;
cancellationSignal.cancel(); cancellationSignal.cancel();
return true; return true;
} }

View File

@@ -23,6 +23,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.util.RotationUtils; import android.util.RotationUtils;
import android.view.DisplayInfo; import android.view.DisplayInfo;
import android.view.Surface; import android.view.Surface;
@@ -130,18 +131,26 @@ public class UdfpsEnrollView extends FrameLayout {
onFingerUp(); onFingerUp();
} }
private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::updateOverlayParams;
/** /**
* setup SensorProperties * setup SensorProperties
*/ */
public void setSensorProperties(FingerprintSensorPropertiesInternal properties) { public void setSensorProperties(FingerprintSensorPropertiesInternal properties) {
mSensorProperties = properties; mSensorProperties = properties;
((ViewGroup) getParent()).getViewTreeObserver().addOnDrawListener( ((ViewGroup) getParent()).getViewTreeObserver().addOnDrawListener(mOnDrawListener);
new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
updateOverlayParams();
} }
});
@Override
protected void onDetachedFromWindow() {
final ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
final ViewTreeObserver observer = parent.getViewTreeObserver();
if (observer != null) {
observer.removeOnDrawListener(mOnDrawListener);
}
}
super.onDetachedFromWindow();
} }
private void onSensorRectUpdated() { private void onSensorRectUpdated() {
@@ -168,6 +177,10 @@ public class UdfpsEnrollView extends FrameLayout {
} }
RelativeLayout parent = ((RelativeLayout) getParent()); RelativeLayout parent = ((RelativeLayout) getParent());
if (parent == null) {
Log.e(TAG, "Fail to updateDimensions for " + this + ", parent null");
return;
}
final int[] coords = parent.getLocationOnScreen(); final int[] coords = parent.getLocationOnScreen();
final int parentLeft = coords[0]; final int parentLeft = coords[0];
final int parentTop = coords[1]; final int parentTop = coords[1];

View File

@@ -130,7 +130,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroChooseLock_landscape() { fun testIntroChooseLock_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroChooseLock() testIntroChooseLock()
} }
@@ -193,7 +193,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_withUdfps_clickStart_landscape() { fun testIntroWithGkPwHandle_withUdfps_clickStart_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_withUdfps_clickStart() testIntroWithGkPwHandle_withUdfps_clickStart()
} }
@@ -226,7 +226,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_withUdfps_clickLottie_landscape() { fun testIntroWithGkPwHandle_withUdfps_clickLottie_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_withUdfps_clickLottie() testIntroWithGkPwHandle_withUdfps_clickLottie()
} }
@@ -256,7 +256,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_withSfps_landscape() { fun testIntroWithGkPwHandle_withSfps_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_withSfps() testIntroWithGkPwHandle_withSfps()
} }
@@ -291,7 +291,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_withRfps_landscape() { fun testIntroWithGkPwHandle_withRfps_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_withRfps() testIntroWithGkPwHandle_withRfps()
} }
@@ -314,7 +314,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_clickNoThanksInIntroPage_landscape() { fun testIntroWithGkPwHandle_clickNoThanksInIntroPage_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_clickNoThanksInIntroPage() testIntroWithGkPwHandle_clickNoThanksInIntroPage()
} }
@@ -344,7 +344,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_clickSkipInFindSensor_landscape() { fun testIntroWithGkPwHandle_clickSkipInFindSensor_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_clickSkipInFindSensor() testIntroWithGkPwHandle_clickSkipInFindSensor()
} }
@@ -382,7 +382,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw_landscape() { fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw()
} }
@@ -418,7 +418,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw_landscape() { fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw()
} }
@@ -449,7 +449,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testEnrollingWithGkPwHandle_landscape() { fun testEnrollingWithGkPwHandle_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testEnrollingWithGkPwHandle() testEnrollingWithGkPwHandle()
} }
@@ -492,7 +492,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testEnrollingIconTouchDialog_withSfps_landscape() { fun testEnrollingIconTouchDialog_withSfps_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testEnrollingIconTouchDialog_withSfps() testEnrollingIconTouchDialog_withSfps()
} }
@@ -534,7 +534,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testEnrollingIconTouchDialog_withRfps_landscape() { fun testEnrollingIconTouchDialog_withRfps_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testEnrollingIconTouchDialog_withRfps() testEnrollingIconTouchDialog_withRfps()
} }
@@ -563,7 +563,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testFindUdfpsWithGkPwHandle_clickStart_landscape() { fun testFindUdfpsWithGkPwHandle_clickStart_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testFindUdfpsWithGkPwHandle_clickStart() testFindUdfpsWithGkPwHandle_clickStart()
} }
@@ -580,7 +580,11 @@ class FingerprintEnrollmentActivityTest {
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue() assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
// rotate device // rotate device
if (runAsLandscape) {
device.setOrientationPortrait()
} else {
device.setOrientationLandscape() device.setOrientationLandscape()
}
device.waitForIdle() device.waitForIdle()
// FindUdfps page (landscape) // FindUdfps page (landscape)
@@ -605,6 +609,12 @@ class FingerprintEnrollmentActivityTest {
assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue() assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
} }
@Test
fun testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack_runAslandscape() {
runAsLandscape = true
testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack()
}
@Test @Test
fun testFindUdfpsWithGkPwHandle_clickLottie() { fun testFindUdfpsWithGkPwHandle_clickLottie() {
Assume.assumeTrue(canAssumeUdfps) Assume.assumeTrue(canAssumeUdfps)
@@ -629,7 +639,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testFindUdfpsWithGkPwHandle_clickLottie_landscape() { fun testFindUdfpsWithGkPwHandle_clickLottie_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testFindUdfpsWithGkPwHandle_clickLottie() testFindUdfpsWithGkPwHandle_clickLottie()
} }
@@ -653,7 +663,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testFindSfpsWithGkPwHandle_landscape() { fun testFindSfpsWithGkPwHandle_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testFindSfpsWithGkPwHandle() testFindSfpsWithGkPwHandle()
} }
@@ -688,7 +698,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testFindRfpsWithGkPwHandle_landscape() { fun testFindRfpsWithGkPwHandle_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testFindRfpsWithGkPwHandle() testFindRfpsWithGkPwHandle()
} }
@@ -712,7 +722,7 @@ class FingerprintEnrollmentActivityTest {
} }
@Test @Test
fun testFindSensorWithGkPwHandle_clickSkipInFindSensor_landscape() { fun testFindSensorWithGkPwHandle_clickSkipInFindSensor_runAslandscape() {
runAsLandscape = true runAsLandscape = true
testFindSensorWithGkPwHandle_clickSkipInFindSensor() testFindSensorWithGkPwHandle_clickSkipInFindSensor()
} }

View File

@@ -18,13 +18,9 @@ package com.android.settings.biometrics2.ui.viewmodel;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP; import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FingerprintErrorDialogAction;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository; import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -82,15 +78,6 @@ public class FingerprintEnrollEnrollingViewModelTest {
); );
} }
@Test
public void testShowErrorDialogLiveData() {
assertThat(mViewModel.getErrorDialogLiveData().getValue()).isEqualTo(null);
final ErrorDialogData data = new ErrorDialogData("errMsg", "errTitle", 0);
mViewModel.showErrorDialog(data);
assertThat(mViewModel.getErrorDialogLiveData().getValue()).isEqualTo(data);
}
@Test @Test
public void testIconTouchDialog() { public void testIconTouchDialog() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData(); final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
@@ -101,20 +88,6 @@ public class FingerprintEnrollEnrollingViewModelTest {
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG); FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG);
} }
@Test
public void testErrorDialogActionLiveData() {
assertThat(mViewModel.getErrorDialogActionLiveData().getValue()).isEqualTo(null);
@FingerprintErrorDialogAction int action =
FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT;
mViewModel.onErrorDialogAction(action);
assertThat(mViewModel.getErrorDialogActionLiveData().getValue()).isEqualTo(action);
action = FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH;
mViewModel.onErrorDialogAction(action);
assertThat(mViewModel.getErrorDialogActionLiveData().getValue()).isEqualTo(action);
}
@Test @Test
public void tesBackPressedScenario() { public void tesBackPressedScenario() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData(); final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2023 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.biometrics2.ui.viewmodel
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollErrorDialogViewModelTest {
private val application = ApplicationProvider.getApplicationContext<Application>()
private var viewModel: FingerprintEnrollErrorDialogViewModel =
FingerprintEnrollErrorDialogViewModel(application, false)
@Before
fun setUp() {
// Make sure viewModel is new for each test
viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
}
@Test
fun testIsDialogNotShownDefaultFalse() {
assertThat(viewModel.isDialogShown).isFalse()
}
@Test
fun testIsSuw() {
assertThat(FingerprintEnrollErrorDialogViewModel(application, false).isSuw).isFalse()
assertThat(FingerprintEnrollErrorDialogViewModel(application, true).isSuw).isTrue()
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testNewDialog() = runTest {
backgroundScope.launch {
mutableListOf<Any>().let { list ->
viewModel.newDialogFlow.toList(list)
assertThat(list.size).isEqualTo(0)
}
mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
val testErrorMsgId = 3456
viewModel.newDialog(testErrorMsgId)
assertThat(viewModel.isDialogShown).isTrue()
viewModel.setResultFlow.toList(list)
assertThat(list.size).isEqualTo(1)
assertThat(list[0]).isEqualTo(testErrorMsgId)
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testTriggerRetry() = runTest {
backgroundScope.launch {
// triggerRetryFlow shall be empty on begin
mutableListOf<Any>().let { list ->
viewModel.triggerRetryFlow.toList(list)
assertThat(list.size).isEqualTo(0)
}
// emit newDialog
mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
viewModel.newDialog(0)
viewModel.triggerRetry()
assertThat(viewModel.isDialogShown).isFalse()
viewModel.setResultFlow.toList(list)
assertThat(list.size).isEqualTo(1)
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testSetResultFinish() = runTest {
backgroundScope.launch {
// setResultFlow shall be empty on begin
mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
viewModel.setResultFlow.toList(list)
assertThat(list.size).isEqualTo(0)
}
// emit FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
viewModel.newDialog(0)
viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
assertThat(viewModel.isDialogShown).isFalse()
viewModel.setResultFlow.toList(list)
assertThat(list.size).isEqualTo(1)
assertThat(list[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
}
// emit FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
viewModel.newDialog(0)
viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT)
assertThat(viewModel.isDialogShown).isFalse()
viewModel.setResultFlow.toList(list)
assertThat(list.size).isEqualTo(1)
assertThat(list[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
}
}
}
}

View File

@@ -108,9 +108,9 @@ public class FingerprintEnrollProgressViewModelTest {
mViewModel.setToken(token); mViewModel.setToken(token);
// Start enrollment // Start enrollment
final boolean ret = mViewModel.startEnrollment(enrollReason); final Object ret = mViewModel.startEnrollment(enrollReason);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
@@ -123,9 +123,9 @@ public class FingerprintEnrollProgressViewModelTest {
mViewModel.setToken(token); mViewModel.setToken(token);
// Start enrollment // Start enrollment
final boolean ret = mViewModel.startEnrollment(enrollReason); final Object ret = mViewModel.startEnrollment(enrollReason);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
@@ -142,9 +142,9 @@ public class FingerprintEnrollProgressViewModelTest {
mViewModel.setToken(token); mViewModel.setToken(token);
// Start enrollment // Start enrollment
final boolean ret = mViewModel.startEnrollment(enrollReason); final Object ret = mViewModel.startEnrollment(enrollReason);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason)); eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
@@ -166,9 +166,9 @@ public class FingerprintEnrollProgressViewModelTest {
@Test @Test
public void testStartEnrollmentFailBecauseOfNoToken() { public void testStartEnrollmentFailBecauseOfNoToken() {
// Start enrollment // Start enrollment
final boolean ret = mViewModel.startEnrollment(ENROLL_FIND_SENSOR); final Object ret = mViewModel.startEnrollment(ENROLL_FIND_SENSOR);
assertThat(ret).isFalse(); assertThat(ret).isNull();
verify(mFingerprintUpdater, never()).enroll(any(byte[].class), verify(mFingerprintUpdater, never()).enroll(any(byte[].class),
any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt()); any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt());
} }
@@ -177,8 +177,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testCancelEnrollment() { public void testCancelEnrollment() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCancellationSignalWrapper.mValue).isNotNull(); assertThat(mCancellationSignalWrapper.mValue).isNotNull();
// Cancel enrollment // Cancel enrollment
@@ -191,8 +191,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testProgressUpdate() { public void testProgressUpdate() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Test default value // Test default value
@@ -228,8 +228,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testProgressUpdateClearHelpMessage() { public void testProgressUpdateClearHelpMessage() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData(); final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData();
final LiveData<EnrollmentStatusMessage> helpMsgLiveData = final LiveData<EnrollmentStatusMessage> helpMsgLiveData =
@@ -271,8 +271,8 @@ public class FingerprintEnrollProgressViewModelTest {
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
// Start enrollment // Start enrollment
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Test default value // Test default value
@@ -308,8 +308,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testGetErrorMessageLiveData() { public void testGetErrorMessageLiveData() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Check default value // Check default value
@@ -330,8 +330,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testGetHelpMessageLiveData() { public void testGetHelpMessageLiveData() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Check default value // Check default value
@@ -352,8 +352,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testGetAcquireLiveData() { public void testGetAcquireLiveData() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Check default value // Check default value
@@ -369,8 +369,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testGetPointerDownLiveData() { public void testGetPointerDownLiveData() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Check default value // Check default value
@@ -387,8 +387,8 @@ public class FingerprintEnrollProgressViewModelTest {
public void testGetPointerUpLiveData() { public void testGetPointerUpLiveData() {
// Start enrollment // Start enrollment
mViewModel.setToken(new byte[] { 1, 2, 3 }); mViewModel.setToken(new byte[] { 1, 2, 3 });
final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
assertThat(ret).isTrue(); assertThat(ret).isNotNull();
assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue).isNotNull();
// Check default value // Check default value