UDFPS Enrollment Refactor (6/N)
Bug fixes Bug: 297082837 Change-Id: I86013007f089e9c57e1f7406f327c001bc4099b4
This commit is contained in:
@@ -184,6 +184,7 @@ public class UdfpsEnrollHelper extends InstrumentedFragment {
|
||||
*/
|
||||
public void onAcquired(boolean isAcquiredGood) {
|
||||
if (mListener != null) {
|
||||
Log.e("JRM", "OnaCquired " + isAcquiredGood + " lastStepIsGood" + animateIfLastStep());
|
||||
mListener.onAcquired(isAcquiredGood && animateIfLastStep());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class DebuggingRepositoryImpl : DebuggingRepository {
|
||||
*/
|
||||
private val isBuildDebuggable = Build.IS_DEBUGGABLE
|
||||
/** This flag indicates if udfps should use debug repos to supply data to its various views. */
|
||||
private val udfpsEnrollmentDebugEnabled = true
|
||||
private val udfpsEnrollmentDebugEnabled = false
|
||||
|
||||
override fun isDebuggingEnabled(): Boolean {
|
||||
return isBuildDebuggable
|
||||
|
||||
@@ -34,10 +34,10 @@ class EnrollStageInteractorImpl() : EnrollStageInteractor {
|
||||
flowOf(
|
||||
mapOf(
|
||||
0.0f to EnrollStageModel.Center,
|
||||
0.25f to EnrollStageModel.Guided,
|
||||
0.5f to EnrollStageModel.Fingertip,
|
||||
0.75f to EnrollStageModel.LeftEdge,
|
||||
0.875f to EnrollStageModel.RightEdge,
|
||||
0.065f to EnrollStageModel.Guided,
|
||||
0.48f to EnrollStageModel.Fingertip,
|
||||
0.584f to EnrollStageModel.LeftEdge,
|
||||
0.792f to EnrollStageModel.RightEdge,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
/** This repository is responsible for collecting all state related to the enroll API. */
|
||||
@@ -106,6 +107,30 @@ class FingerprintEnrollInteractorImpl(
|
||||
streamEnded = true
|
||||
enrollRequestOutstanding.update { false }
|
||||
}
|
||||
|
||||
override fun onUdfpsPointerDown(sensorId: Int) {
|
||||
trySend(FingerEnrollState.PointerDown(sensorId)).onFailure { error ->
|
||||
Log.d(TAG, "onUdfpsPointerDown failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUdfpsPointerUp(sensorId: Int) {
|
||||
trySend(FingerEnrollState.PointerUp(sensorId)).onFailure { error ->
|
||||
Log.d(TAG, "onUdfpsPointerUp failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUdfpsOverlayShown() {
|
||||
trySend(FingerEnrollState.OverlayShown).onFailure { error ->
|
||||
Log.d(TAG, "OverlayShown failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAcquired(isAcquiredGood: Boolean) {
|
||||
trySend(FingerEnrollState.Acquired(isAcquiredGood)).onFailure { error ->
|
||||
Log.d(TAG, "Acquired failed to send, due to $error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cancellationSignal = CancellationSignal()
|
||||
|
||||
@@ -17,17 +17,12 @@
|
||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.OrientationEventListener
|
||||
import com.android.internal.R
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transform
|
||||
|
||||
/** Interactor which provides information about orientation */
|
||||
@@ -35,7 +30,9 @@ interface OrientationInteractor {
|
||||
/** A flow that contains the information about the orientation changing */
|
||||
val orientation: Flow<Int>
|
||||
/**
|
||||
* A flow that contains the rotation info
|
||||
* This indicates the surface rotation that hte view is currently in. For instance its possible to
|
||||
* rotate a view to 90 degrees but for it to still be portrait mode. In this case, this flow
|
||||
* should emit that we are in rotation 0 (SurfaceView.Rotation_0)
|
||||
*/
|
||||
val rotation: Flow<Int>
|
||||
/**
|
||||
@@ -50,8 +47,7 @@ interface OrientationInteractor {
|
||||
fun getRotationFromDefault(rotation: Int): Int
|
||||
}
|
||||
|
||||
class OrientationInteractorImpl(private val context: Context, activityScope: CoroutineScope) :
|
||||
OrientationInteractor {
|
||||
class OrientationInteractorImpl(private val context: Context) : OrientationInteractor {
|
||||
|
||||
override val orientation: Flow<Int> = callbackFlow {
|
||||
val orientationEventListener =
|
||||
@@ -62,9 +58,12 @@ class OrientationInteractorImpl(private val context: Context, activityScope: Cor
|
||||
}
|
||||
orientationEventListener.enable()
|
||||
awaitClose { orientationEventListener.disable() }
|
||||
}.shareIn(activityScope, SharingStarted.Eagerly, replay = 1)
|
||||
}
|
||||
|
||||
override val rotation: Flow<Int> = orientation.transform { emit(context.display!!.rotation) }
|
||||
override val rotation: Flow<Int> =
|
||||
orientation.transform {
|
||||
emit(context.display!!.rotation)
|
||||
}
|
||||
|
||||
override val rotationFromDefault: Flow<Int> = rotation.map { getRotationFromDefault(it) }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.biometrics.fingerprint2.domain.interactor
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -69,6 +70,7 @@ class UdfpsEnrollInteractorImpl(
|
||||
|
||||
override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
|
||||
val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
|
||||
Log.e("JRM", "guided enroll step $index")
|
||||
_guidedEnrollment.update { guidedEnrollmentPoints[index] }
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ sealed class FingerEnrollState {
|
||||
data class Acquired(val acquiredGood: Boolean) : FingerEnrollState()
|
||||
|
||||
/** Indicates a pointer down event has occurred */
|
||||
data object PointerDown : FingerEnrollState()
|
||||
data class PointerDown(val fingerId: Int) : FingerEnrollState()
|
||||
|
||||
/** Indicates a pointer up event has occurred */
|
||||
data object PointerUp : FingerEnrollState()
|
||||
data class PointerUp(val fingerId: Int) : FingerEnrollState()
|
||||
|
||||
/** Indicates the overlay has shown */
|
||||
data object OverlayShown : FingerEnrollState()
|
||||
|
||||
@@ -72,6 +72,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.fragment.RFPSEnrollFragment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment.UdfpsEnrollFragment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsLastStepViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
|
||||
@@ -126,6 +127,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
private lateinit var fingerprintFlowViewModel: FingerprintFlowViewModel
|
||||
private lateinit var fingerprintEnrollConfirmationViewModel:
|
||||
FingerprintEnrollConfirmationViewModel
|
||||
private lateinit var udfpsLastStepViewModel: UdfpsLastStepViewModel
|
||||
private lateinit var udfpsViewModel: UdfpsViewModel
|
||||
private lateinit var enrollStageInteractor: EnrollStageInteractor
|
||||
private val coroutineDispatcher = Dispatchers.Default
|
||||
@@ -320,7 +322,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
foldStateInteractor = FoldStateInteractorImpl(context)
|
||||
foldStateInteractor.onConfigurationChange(resources.configuration)
|
||||
|
||||
orientationInteractor = OrientationInteractorImpl(context, lifecycleScope)
|
||||
orientationInteractor = OrientationInteractorImpl(context)
|
||||
vibrationInteractor =
|
||||
VibrationInteractorImpl(context.getSystemService(Vibrator::class.java)!!, context)
|
||||
|
||||
@@ -373,11 +375,15 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
fingerprintEnrollEnrollingViewModel,
|
||||
navigationViewModel,
|
||||
orientationInteractor,
|
||||
fingerprintManagerInteractor,
|
||||
),
|
||||
)[RFPSViewModel::class.java]
|
||||
|
||||
enrollStageInteractor = EnrollStageInteractorImpl()
|
||||
|
||||
udfpsLastStepViewModel =
|
||||
UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor)
|
||||
|
||||
udfpsViewModel =
|
||||
ViewModelProvider(
|
||||
this,
|
||||
@@ -393,6 +399,9 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
backgroundViewModel,
|
||||
fingerprintSensorRepo,
|
||||
udfpsEnrollInteractor,
|
||||
fingerprintManagerInteractor,
|
||||
udfpsLastStepViewModel,
|
||||
accessibilityInteractor,
|
||||
),
|
||||
)[UdfpsViewModel::class.java]
|
||||
|
||||
@@ -456,7 +465,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
|
||||
step.exitTransition.toAnimation(),
|
||||
)
|
||||
.setReorderingAllowed(true)
|
||||
.add(R.id.fragment_container_view, theClass::class.java, null)
|
||||
.replace(R.id.fragment_container_view, theClass::class.java, null)
|
||||
.commit()
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.TRANSITION_FINISHED,
|
||||
|
||||
@@ -20,15 +20,18 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintAction
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep.Enrollment
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -41,6 +44,7 @@ class RFPSViewModel(
|
||||
private val fingerprintEnrollViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||
orientationInteractor: OrientationInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _textViewIsVisible = MutableStateFlow(false)
|
||||
@@ -52,7 +56,16 @@ class RFPSViewModel(
|
||||
/** Indicates if the icon should be animating or not */
|
||||
val shouldAnimateIcon = _shouldAnimateIcon
|
||||
|
||||
private var enrollFlow: Flow<FingerEnrollState?> = fingerprintEnrollViewModel.enrollFlow
|
||||
private var enrollFlow: Flow<FingerEnrollState?> =
|
||||
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||
fingerprintEnrollViewModel.enrollFlow
|
||||
) { props, enroll ->
|
||||
if (props.sensorType == FingerprintSensorType.REAR) {
|
||||
enroll
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enroll progress message with a replay of size 1 allowing for new subscribers to get the most
|
||||
@@ -149,6 +162,7 @@ class RFPSViewModel(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val navigationViewModel: FingerprintNavigationViewModel,
|
||||
private val orientationInteractor: OrientationInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -157,6 +171,7 @@ class RFPSViewModel(
|
||||
fingerprintEnrollEnrollingViewModel,
|
||||
navigationViewModel,
|
||||
orientationInteractor,
|
||||
fingerprintManager,
|
||||
)
|
||||
as T
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.MotionEvent.ACTION_HOVER_MOVE
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -40,7 +42,6 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enroll
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||
import com.google.android.setupdesign.GlifLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -71,10 +72,8 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
val fragment = this
|
||||
lottie = view.findViewById(R.id.illustration_lottie)!!
|
||||
udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
|
||||
val titleTextView = view.findViewById<TextView>(R.id.title)!!
|
||||
val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
|
||||
val glifLayout: GlifLayout = view.findViewById(R.id.glif_layout)!!
|
||||
|
||||
val glifLayout = view.findViewById<GlifLayout>(R.id.dummy_glif_layout)!!
|
||||
val backgroundColor = glifLayout.backgroundBaseColor
|
||||
val window = requireActivity().window
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
@@ -83,6 +82,10 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
window.statusBarColor = color
|
||||
view.setBackgroundColor(color)
|
||||
|
||||
view.findViewById<Button>(R.id.skip)?.apply {
|
||||
setOnClickListener { viewModel.negativeButtonClicked() }
|
||||
}
|
||||
|
||||
udfpsEnrollView.setFinishAnimationCompleted { viewModel.finishedSuccessfully() }
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
@@ -92,32 +95,41 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
udfpsEnrollView.setSensorRect(sensor.sensorBounds, sensor.sensorType)
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.descriptionText.collect {
|
||||
if (it != null) {
|
||||
it.toResource()?.let { text -> descriptionTextView.setText(text) }
|
||||
} else {
|
||||
descriptionTextView.text = ""
|
||||
launch { viewModel.overlayShown.collect { udfpsEnrollView.overlayShown() } }
|
||||
launch { viewModel.headerText.collect { glifLayout.setHeaderText(it.toResource()) } }
|
||||
launch {
|
||||
viewModel.userInteractedWithSensor.collect {
|
||||
if (!it) {
|
||||
glifLayout.setHeaderText(R.string.security_settings_fingerprint_enroll_udfps_title)
|
||||
glifLayout.setDescriptionText(R.string.security_settings_udfps_enroll_start_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
|
||||
launch {
|
||||
viewModel.descriptionText.collect {
|
||||
if (it != null) {
|
||||
it.toResource()?.let { text -> glifLayout.setDescriptionText(text) }
|
||||
} else {
|
||||
glifLayout.descriptionText = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
viewModel.shouldShowLottie.collect {
|
||||
lottie.visibility = if (it) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.lottie.collect { lottieModel ->
|
||||
if (lottie.visibility == View.GONE) {
|
||||
return@collect
|
||||
}
|
||||
val resource = lottieModel.toResource()
|
||||
if (resource != null) {
|
||||
glifLayout.descriptionTextView.visibility = View.GONE
|
||||
LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
|
||||
comp?.let { composition ->
|
||||
lottie.setComposition(composition)
|
||||
@@ -126,27 +138,24 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glifLayout.descriptionTextView.visibility = View.VISIBLE
|
||||
lottie.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.accessibilityEnabled.collect { enabled ->
|
||||
udfpsEnrollView.setAccessibilityEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
launch {
|
||||
viewModel.enrollState.collect {
|
||||
Log.d(TAG, "EnrollEvent $it")
|
||||
if (it is FingerEnrollState.EnrollError) {
|
||||
try {
|
||||
FingerprintErrorDialog.showInstance(it, fragment)
|
||||
viewModel.errorDialogShown(it)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Exception occurred $exception")
|
||||
}
|
||||
@@ -156,19 +165,40 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) }
|
||||
launch { viewModel.progressSaved.collect { udfpsEnrollView.onEnrollProgressSaved(it) } }
|
||||
|
||||
launch { viewModel.guidedEnrollment.collect { udfpsEnrollView.updateGuidedEnrollment(it) } }
|
||||
launch {
|
||||
viewModel.guidedEnrollmentSaved.collect { udfpsEnrollView.onGuidedPointSaved(it) }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.guidedEnrollment.collect {
|
||||
glifLayout.post { udfpsEnrollView.updateGuidedEnrollment(it) }
|
||||
launch { viewModel.shouldDrawIcon.collect { udfpsEnrollView.shouldDrawIcon(it) } }
|
||||
|
||||
// Hack to get the final step of enroll progress to animate.
|
||||
launch {
|
||||
viewModel.udfpsLastStepViewModel.shouldAnimateCompletion.collect {
|
||||
Log.d(TAG, "Sending fake last enroll event $it")
|
||||
udfpsEnrollView.onUdfpsEvent(it)
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.guidedEnrollmentSaved.collect {
|
||||
glifLayout.post { udfpsEnrollView.onGuidedPointSaved(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.DESTROYED) { viewModel.stopEnrollment() }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.isLandscape.collect {
|
||||
if (it) {
|
||||
changeViewToLandscape()
|
||||
}
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.isReverseLandscape.collect {
|
||||
if (it) {
|
||||
changeViewToReverseLandscape()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,12 +213,70 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
viewModel.readyForEnrollment()
|
||||
}
|
||||
|
||||
private fun changeViewToReverseLandscape() {
|
||||
Log.d(TAG, "changeViewToReverseLandscape")
|
||||
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||
val headerView =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||
)
|
||||
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||
val parent = headerView!!.parent as ViewGroup
|
||||
val sudContentFrame =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||
)!!
|
||||
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||
|
||||
parent.removeView(headerView)
|
||||
parent.removeView(sudContentFrame)
|
||||
parent.addView(sudContentFrame)
|
||||
parent.addView(headerView)
|
||||
|
||||
unclipSubviewsFromParent(udfpsContainer)
|
||||
udfpsEnrollView.requestLayout()
|
||||
}
|
||||
|
||||
private fun changeViewToLandscape() {
|
||||
Log.d(TAG, "changeViewToLandscape")
|
||||
|
||||
val glifContainer = requireView().findViewById<GlifLayout>(R.id.glif_layout)!!
|
||||
val headerView =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_header_area
|
||||
)
|
||||
// The landscape_header_area nad landscape_content_area should have the same parent
|
||||
val parent = headerView!!.parent as ViewGroup
|
||||
val sudContentFrame =
|
||||
glifContainer.findViewById<View>(
|
||||
com.google.android.setupdesign.R.id.sud_landscape_content_area
|
||||
)!!
|
||||
|
||||
parent.removeView(headerView)
|
||||
parent.removeView(sudContentFrame)
|
||||
parent.addView(headerView)
|
||||
parent.addView(sudContentFrame)
|
||||
|
||||
val udfpsContainer = requireView().findViewById<FrameLayout>(R.id.layout_container)!!
|
||||
unclipSubviewsFromParent(udfpsContainer)
|
||||
udfpsEnrollView.requestLayout()
|
||||
}
|
||||
|
||||
private fun unclipSubviewsFromParent(view: View) {
|
||||
var currParent = view.parent
|
||||
while (currParent is ViewGroup) {
|
||||
currParent.clipChildren = false
|
||||
currParent.clipToPadding = false
|
||||
currParent = currParent.parent
|
||||
}
|
||||
}
|
||||
|
||||
private fun HeaderText.toResource(): Int {
|
||||
return when (this.enrollStageModel) {
|
||||
EnrollStageModel.Center,
|
||||
EnrollStageModel.Guided,
|
||||
EnrollStageModel.Fingertip,
|
||||
EnrollStageModel.Unknown -> R.string.security_settings_udfps_enroll_fingertip_title
|
||||
EnrollStageModel.Guided -> R.string.security_settings_fingerprint_enroll_repeat_title
|
||||
EnrollStageModel.Fingertip -> R.string.security_settings_udfps_enroll_fingertip_title
|
||||
EnrollStageModel.Unknown -> R.string.security_settings_fingerprint_enroll_udfps_title
|
||||
EnrollStageModel.LeftEdge -> R.string.security_settings_udfps_enroll_left_edge_title
|
||||
EnrollStageModel.RightEdge -> R.string.security_settings_udfps_enroll_right_edge_title
|
||||
}
|
||||
@@ -218,6 +306,5 @@ class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enro
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UDFPSEnrollFragment"
|
||||
private val navStep = FingerprintNavigationStep.Enrollment::class
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model
|
||||
|
||||
import android.graphics.PointF
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||
|
||||
data class UdfpsSensorLocation(val sensorLocation: FingerprintSensor, val offset: PointF?)
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintVibrationEffects
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollEnrollingViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* This class is responsible for determining if we should animate the last step of an enrollment. It
|
||||
* seems to be due to poor hardware implementation that the last step of a UDFPS enrollment takes
|
||||
* much longer then normal (likely due to to saving the whole enrollment/or some kind of
|
||||
* verification)
|
||||
*
|
||||
* Because of this, we will use the information of if a fingerprint was acquired(good) + enrollment
|
||||
* has reached the last step
|
||||
*/
|
||||
class UdfpsLastStepViewModel(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val lastStep: MutableStateFlow<FingerEnrollState.EnrollProgress?> = MutableStateFlow(null)
|
||||
private val stepSize: MutableStateFlow<Int?> = MutableStateFlow(null)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val steps =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.distinctUntilChanged()
|
||||
.take(2)
|
||||
.toList()
|
||||
stepSize.update { steps[0].remainingSteps - steps[1].remainingSteps }
|
||||
lastStep.update { FingerEnrollState.EnrollProgress(0, steps[0].totalStepsRequired) }
|
||||
}
|
||||
}
|
||||
|
||||
private val enrollProgress =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow.filterIsInstance<
|
||||
FingerEnrollState.EnrollProgress
|
||||
>()
|
||||
|
||||
/** Indicates if we should animate the completion of udfps enrollment. */
|
||||
val shouldAnimateCompletion =
|
||||
enrollProgress
|
||||
.transform { it ->
|
||||
if (it.remainingSteps == stepSize.value) {
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
.filterIsInstance<FingerEnrollState.Acquired>()
|
||||
.filter { acquired -> acquired.acquiredGood }
|
||||
.collect {
|
||||
vibrationInteractor.vibrate(
|
||||
FingerprintVibrationEffects.UdfpsSuccess,
|
||||
"UdfpsLastStepViewModel",
|
||||
)
|
||||
|
||||
emit(lastStep.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
|
||||
class UdfpsLastStepViewModelFactory(
|
||||
private val fingerprintEnrollEnrollingViewModel: FingerprintEnrollEnrollingViewModel,
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return UdfpsLastStepViewModel(fingerprintEnrollEnrollingViewModel, vibrationInteractor) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,10 @@ import android.view.Surface
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||
import com.android.settings.biometrics.fingerprint2.data.model.EnrollStageModel
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository
|
||||
import com.android.settings.biometrics.fingerprint2.data.repository.SimulatedTouchEventsRepository
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.AccessibilityInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.DebuggingInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.DisplayDensityInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.EnrollStageInteractor
|
||||
@@ -32,6 +33,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Fingerprin
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.UdfpsEnrollInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.domain.interactor.VibrationInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.domain.interactor.FingerprintManagerInteractor
|
||||
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.DescriptionText
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.model.HeaderText
|
||||
@@ -41,6 +43,7 @@ import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Fing
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
|
||||
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.combineTransform
|
||||
@@ -51,6 +54,8 @@ import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
|
||||
@@ -66,13 +71,25 @@ class UdfpsViewModel(
|
||||
backgroundViewModel: BackgroundViewModel,
|
||||
sensorRepository: FingerprintSensorRepository,
|
||||
udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||
fingerprintManager: FingerprintManagerInteractor,
|
||||
val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||
accessibilityInteractor: AccessibilityInteractor,
|
||||
) : ViewModel() {
|
||||
|
||||
private val isSetupWizard = flowOf(false)
|
||||
private var shouldResetErollment = false
|
||||
|
||||
private var _enrollState: Flow<FingerEnrollState?> =
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
fingerprintManager.sensorPropertiesInternal.filterNotNull().combine(
|
||||
fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
) { props, enroll ->
|
||||
if (props.sensorType.isUdfps()) {
|
||||
enroll
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/** The current state of the enrollment. */
|
||||
var enrollState: Flow<FingerEnrollState> =
|
||||
combine(fingerprintEnrollEnrollingViewModel.enrollFlowShouldBeRunning, _enrollState) {
|
||||
@@ -86,48 +103,59 @@ class UdfpsViewModel(
|
||||
}
|
||||
.filterNotNull()
|
||||
|
||||
/** Indicates that overlay has been shown */
|
||||
val overlayShown =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.OverlayShown>()
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
private var _userInteractedWithSensor = MutableStateFlow(false)
|
||||
|
||||
/**
|
||||
* This indicates whether the user has interacted with the sensor or not. This indicates if we are
|
||||
* in the initial state of the UI.
|
||||
*/
|
||||
val userInteractedWithSensor: Flow<Boolean> =
|
||||
enrollState.transform {
|
||||
val interactiveMessage =
|
||||
when (it) {
|
||||
is FingerEnrollState.Acquired,
|
||||
is FingerEnrollState.EnrollError,
|
||||
is FingerEnrollState.EnrollHelp,
|
||||
is FingerEnrollState.EnrollProgress,
|
||||
is FingerEnrollState.PointerDown,
|
||||
is FingerEnrollState.PointerUp -> true
|
||||
else -> false
|
||||
}
|
||||
val hasPreviouslyInteracted = _userInteractedWithSensor.value or interactiveMessage
|
||||
_userInteractedWithSensor.update { hasPreviouslyInteracted }
|
||||
emit(hasPreviouslyInteracted)
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the property sensor information. This is typically used to recreate views that must be
|
||||
* aligned with the sensor.
|
||||
*/
|
||||
val sensorLocation = sensorRepository.fingerprintSensor
|
||||
|
||||
/** Indicates if accessibility is enabled */
|
||||
val accessibilityEnabled = flowOf(true).shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
enrollState
|
||||
.combine(accessibilityEnabled) { event, isEnabled -> Pair(event, isEnabled) }
|
||||
.collect {
|
||||
if (
|
||||
when (it.first) {
|
||||
is FingerEnrollState.EnrollError -> true
|
||||
is FingerEnrollState.EnrollHelp -> it.second
|
||||
is FingerEnrollState.EnrollProgress -> true
|
||||
else -> false
|
||||
}
|
||||
) {
|
||||
vibrate(it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This indicates at which point the UI should offset the fingerprint sensor icon for guided
|
||||
* enrollment.
|
||||
*/
|
||||
/** Indicates a step of guided enrollment, the ui should animate the icon to the new location. */
|
||||
val guidedEnrollment: Flow<PointF> =
|
||||
udfpsEnrollInteractor.guidedEnrollmentOffset.distinctUntilChanged()
|
||||
udfpsEnrollInteractor.guidedEnrollmentOffset
|
||||
.distinctUntilChanged()
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||
|
||||
/** The saved version of [guidedEnrollment] */
|
||||
val guidedEnrollmentSaved: Flow<PointF> =
|
||||
guidedEnrollment.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
private var _lastOrientation: Int? = null
|
||||
|
||||
/** In case of rotations we should ensure the UI does not re-animate the last state. */
|
||||
private val shouldReplayLastEvent =
|
||||
orientationInteractor.rotation.transform {
|
||||
if (_lastOrientation != null && _lastOrientation!! != it) {
|
||||
emit(true)
|
||||
} else {
|
||||
emit(false)
|
||||
}
|
||||
_lastOrientation = it
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the saved progress, this is for when views are recreated and need saved state for the
|
||||
@@ -136,8 +164,32 @@ class UdfpsViewModel(
|
||||
var progressSaved: Flow<FingerEnrollState.EnrollProgress> =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
.combineTransform(shouldReplayLastEvent) { enroll, shouldReplay ->
|
||||
if (shouldReplay) {
|
||||
emit(enroll)
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
/** Indicates if accessibility is enabled */
|
||||
val accessibilityEnabled =
|
||||
accessibilityInteractor.isAccessibilityEnabled.shareIn(
|
||||
this.viewModelScope,
|
||||
SharingStarted.Eagerly,
|
||||
replay = 1,
|
||||
)
|
||||
|
||||
/** Indicates if we are in reverse landscape orientation. */
|
||||
val isReverseLandscape =
|
||||
orientationInteractor.rotation
|
||||
.transform { emit(it == Surface.ROTATION_270) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** Indicates if we are in the landscape orientation */
|
||||
val isLandscape =
|
||||
orientationInteractor.rotation
|
||||
.transform { emit(it == Surface.ROTATION_90) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** This sends touch exploration events only used for debugging purposes. */
|
||||
val touchExplorationDebug: Flow<Point> =
|
||||
@@ -170,6 +222,18 @@ class UdfpsViewModel(
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
/** The saved version of [guidedEnrollment] */
|
||||
val guidedEnrollmentSaved: Flow<PointF> =
|
||||
combineTransform(guidedEnrollment, shouldReplayLastEvent, enrollStage) {
|
||||
point,
|
||||
shouldReplay,
|
||||
stage ->
|
||||
if (shouldReplay && stage is EnrollStageModel.Guided) {
|
||||
emit(point)
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
enrollState
|
||||
@@ -200,7 +264,7 @@ class UdfpsViewModel(
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
backgroundViewModel.background.filter { true }.collect { didGoToBackground() }
|
||||
backgroundViewModel.background.filter { it }.collect { didGoToBackground() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,20 +274,12 @@ class UdfpsViewModel(
|
||||
displayDensityInteractor.displayDensity,
|
||||
displayDensityInteractor.defaultDisplayDensity,
|
||||
displayDensityInteractor.fontScale,
|
||||
orientationInteractor.rotation,
|
||||
) { currDisplayDensity, defaultDisplayDensity, fontScale, rotation ->
|
||||
val canShowLottieForRotation =
|
||||
when (rotation) {
|
||||
Surface.ROTATION_0 -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
canShowLottieForRotation &&
|
||||
if (fontScale > 1.0f) {
|
||||
false
|
||||
} else {
|
||||
defaultDisplayDensity == currDisplayDensity
|
||||
}
|
||||
) { currDisplayDensity, defaultDisplayDensity, fontScale ->
|
||||
if (fontScale > 1.0f) {
|
||||
false
|
||||
} else {
|
||||
defaultDisplayDensity == currDisplayDensity
|
||||
}
|
||||
}
|
||||
.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
|
||||
|
||||
@@ -234,6 +290,18 @@ class UdfpsViewModel(
|
||||
}
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
|
||||
/** Indicates if we should or shold not draw the fingerprint icon */
|
||||
val shouldDrawIcon: Flow<Boolean> =
|
||||
enrollState.transform { state ->
|
||||
when (state) {
|
||||
is FingerEnrollState.EnrollProgress,
|
||||
is FingerEnrollState.EnrollError,
|
||||
is FingerEnrollState.PointerUp -> emit(true)
|
||||
is FingerEnrollState.PointerDown -> emit(false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private val shouldClearDescriptionText = enrollStage.map { it is EnrollStageModel.Unknown }
|
||||
|
||||
/** The description text for UDFPS enrollment */
|
||||
@@ -267,12 +335,12 @@ class UdfpsViewModel(
|
||||
|
||||
/** Indicates the negative button has been clicked */
|
||||
fun negativeButtonClicked() {
|
||||
doReset()
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.NEGATIVE_BUTTON_PRESSED,
|
||||
navStep,
|
||||
"$TAG#negativeButtonClicked",
|
||||
)
|
||||
doReset()
|
||||
}
|
||||
|
||||
/** Indicates that an enrollment was completed */
|
||||
@@ -282,7 +350,7 @@ class UdfpsViewModel(
|
||||
}
|
||||
|
||||
/** Indicates that the application went to the background. */
|
||||
private fun didGoToBackground() {
|
||||
fun didGoToBackground() {
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.DID_GO_TO_BACKGROUND,
|
||||
navStep,
|
||||
@@ -293,11 +361,7 @@ class UdfpsViewModel(
|
||||
|
||||
private fun doReset() {
|
||||
_enrollState = fingerprintEnrollEnrollingViewModel.enrollFlow
|
||||
progressSaved =
|
||||
enrollState
|
||||
.filterIsInstance<FingerEnrollState.EnrollProgress>()
|
||||
.filterNotNull()
|
||||
.shareIn(this.viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||
_userInteractedWithSensor.update { false }
|
||||
}
|
||||
|
||||
/** The lottie that should be shown for UDFPS Enrollment */
|
||||
@@ -320,6 +384,15 @@ class UdfpsViewModel(
|
||||
vibrationInteractor.vibrate(vibrationEvent, "UdfpsEnrollFragment")
|
||||
}
|
||||
|
||||
/** Indicates an error sent by the HAL has been acknowledged by the user */
|
||||
fun errorDialogShown(it: FingerEnrollState.EnrollError) {
|
||||
navigationViewModel.update(
|
||||
FingerprintAction.USER_CLICKED_FINISH,
|
||||
navStep,
|
||||
"${TAG}#userClickedStopEnrollingDialog",
|
||||
)
|
||||
}
|
||||
|
||||
class UdfpsEnrollmentFactory(
|
||||
private val vibrationInteractor: VibrationInteractor,
|
||||
private val displayDensityInteractor: DisplayDensityInteractor,
|
||||
@@ -332,6 +405,9 @@ class UdfpsViewModel(
|
||||
private val backgroundViewModel: BackgroundViewModel,
|
||||
private val sensorRepository: FingerprintSensorRepository,
|
||||
private val udfpsEnrollInteractor: UdfpsEnrollInteractor,
|
||||
private val fingerprintManager: FingerprintManagerInteractor,
|
||||
private val udfpsLastStepViewModel: UdfpsLastStepViewModel,
|
||||
private val accessibilityInteractor: AccessibilityInteractor,
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -348,6 +424,9 @@ class UdfpsViewModel(
|
||||
backgroundViewModel,
|
||||
sensorRepository,
|
||||
udfpsEnrollInteractor,
|
||||
fingerprintManager,
|
||||
udfpsLastStepViewModel,
|
||||
accessibilityInteractor,
|
||||
)
|
||||
as T
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ import android.util.PathParser
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.graphics.toRect
|
||||
import androidx.core.graphics.toRectF
|
||||
import com.android.settings.R
|
||||
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||
import kotlin.math.sin
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,6 @@ import kotlin.math.sin
|
||||
* various stages of enrollment
|
||||
*/
|
||||
class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
|
||||
private var targetAnimationDuration: Long = TARGET_ANIM_DURATION_LONG
|
||||
private var targetAnimatorSet: AnimatorSet? = null
|
||||
private val movingTargetFpIcon: Drawable
|
||||
private val fingerprintDrawable: ShapeDrawable
|
||||
@@ -55,7 +54,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
@ColorInt private var movingTargetFill = 0
|
||||
private var currentScale = 1.0f
|
||||
private var alpha = 0
|
||||
private var guidedEnrollmentOffset: PointF? = null
|
||||
private var stopDrawing = false
|
||||
|
||||
/**
|
||||
* This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt]
|
||||
@@ -63,15 +62,12 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
private val sensorRectBounds: Rect = Rect()
|
||||
|
||||
/**
|
||||
* The following values are used to describe where the icon should be drawn. [currX] and [currY]
|
||||
* are changed based on the current guided enrollment step which is given by the
|
||||
* [UdfpsEnrollHelperV2]
|
||||
* The following values are used to describe where the icon should be drawn. [sensorLeftOffset]
|
||||
* and [sensorTopOffset] are changed based on the current guided enrollment step which is given by
|
||||
* the [UdfpsEnrollHelperV2]
|
||||
*/
|
||||
private var currX = 0f
|
||||
private var currY = 0f
|
||||
|
||||
private var sensorWidth = 0f
|
||||
private var sensorHeight = 0f
|
||||
private var sensorLeftOffset = 0f
|
||||
private var sensorTopOffset = 0f
|
||||
|
||||
init {
|
||||
fingerprintDrawable = createUdfpsIcon(context)
|
||||
@@ -131,29 +127,35 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
* The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
|
||||
* with respect to its root frameview
|
||||
*/
|
||||
fun drawSensorRectAt(sensorRect: Rect) {
|
||||
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect($sensorRect)")
|
||||
fun drawSensorRectAt(overlayParams: UdfpsOverlayParams) {
|
||||
Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect(${overlayParams.sensorBounds})")
|
||||
val sensorRect = overlayParams.sensorBounds
|
||||
sensorRectBounds.set(sensorRect)
|
||||
fingerprintDrawable.bounds = sensorRect
|
||||
movingTargetFpIcon.bounds = sensorRect
|
||||
currX = sensorRect.left.toFloat()
|
||||
currY = sensorRect.top.toFloat()
|
||||
sensorWidth = (sensorRect.right - sensorRect.left).toFloat()
|
||||
sensorHeight = (sensorRect.bottom - sensorRect.top).toFloat()
|
||||
|
||||
// End existing animation if we get an update of the sensor rect.
|
||||
targetAnimatorSet?.end()
|
||||
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
/** Stop drawing the fingerprint icon. */
|
||||
fun stopDrawing() {
|
||||
alpha = 0
|
||||
stopDrawing = true
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
/** Resume drawing the fingerprint icon */
|
||||
fun startDrawing() {
|
||||
alpha = 255
|
||||
stopDrawing = false
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (stopDrawing) {
|
||||
return
|
||||
}
|
||||
movingTargetFpIcon.alpha = alpha
|
||||
fingerprintDrawable.setAlpha(alpha)
|
||||
sensorOutlinePaint.setAlpha(alpha)
|
||||
@@ -165,23 +167,28 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
fingerprintDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
private fun getCurrLocation(): RectF =
|
||||
RectF(currX, currY, currX + sensorWidth, currY + sensorHeight)
|
||||
private fun getCurrLocation(): RectF {
|
||||
val x = sensorRectBounds.left + sensorLeftOffset
|
||||
val y = sensorRectBounds.top + sensorTopOffset
|
||||
return RectF(x, y, x + sensorRectBounds.width(), y + sensorRectBounds.height())
|
||||
}
|
||||
|
||||
private fun animateMovement(currentBounds: Rect, offsetRect: RectF, scaleMovement: Boolean) {
|
||||
if (currentBounds.equals(offsetRect)) {
|
||||
private fun animateMovement(leftOffset: Float, topOffset: Float, scaleMovement: Boolean) {
|
||||
if (leftOffset == sensorLeftOffset && topOffset == sensorTopOffset) {
|
||||
return
|
||||
}
|
||||
|
||||
val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left)
|
||||
val currLocation = getCurrLocation()
|
||||
|
||||
val xAnimator = ValueAnimator.ofFloat(currLocation.left - sensorRectBounds.left, leftOffset)
|
||||
xAnimator.addUpdateListener {
|
||||
currX = it.animatedValue as Float
|
||||
sensorLeftOffset = it.animatedValue as Float
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
val yAnimator = ValueAnimator.ofFloat(currentBounds.top.toFloat(), offsetRect.top)
|
||||
val yAnimator = ValueAnimator.ofFloat(currLocation.top - sensorRectBounds.top, topOffset)
|
||||
yAnimator.addUpdateListener {
|
||||
currY = it.animatedValue as Float
|
||||
sensorTopOffset = it.animatedValue as Float
|
||||
invalidateSelf()
|
||||
}
|
||||
val animators = mutableListOf(xAnimator, yAnimator)
|
||||
@@ -199,6 +206,7 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
animators.add(scaleAnimator)
|
||||
}
|
||||
|
||||
targetAnimatorSet?.cancel()
|
||||
targetAnimatorSet = AnimatorSet()
|
||||
|
||||
targetAnimatorSet?.let {
|
||||
@@ -209,51 +217,14 @@ class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets animation time to 0. This typically happens after an activity recreation, we don't
|
||||
* want to re-animate the progress/success animation with the default timer
|
||||
*/
|
||||
private fun setAnimationTimeToZero() {
|
||||
targetAnimationDuration = 0
|
||||
}
|
||||
|
||||
/** This sets animation timers back to normal, this happens after we have */
|
||||
private fun restoreAnimationTime() {
|
||||
targetAnimationDuration = TARGET_ANIM_DURATION_LONG
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a change to guided enrollment has occurred. Also indicates if we are recreating the
|
||||
* view, in which case their is no need to animate the icon to whatever position it was in.
|
||||
*/
|
||||
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
||||
guidedEnrollmentOffset = point
|
||||
if (isRecreating) {
|
||||
setAnimationTimeToZero()
|
||||
} else {
|
||||
restoreAnimationTime()
|
||||
}
|
||||
|
||||
val currentBounds = getCurrLocation().toRect()
|
||||
val offset = guidedEnrollmentOffset
|
||||
if (offset?.x != 0f && offset?.y != 0f) {
|
||||
val targetRect = Rect(sensorRectBounds).toRectF()
|
||||
// This is the desired location of the sensor rect, the [EnrollHelper]
|
||||
// offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
|
||||
targetRect.offset(offset!!.x, offset!!.y)
|
||||
val shouldAnimateMovement = !currentBounds.equals(targetRect)
|
||||
if (shouldAnimateMovement) {
|
||||
targetAnimatorSet?.cancel()
|
||||
animateMovement(currentBounds, targetRect, true)
|
||||
} else {
|
||||
// If we are not offsetting the sensor, move it back to its original place
|
||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
||||
}
|
||||
} else {
|
||||
// If we are not offsetting the sensor, move it back to its original place
|
||||
animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
|
||||
}
|
||||
invalidateSelf()
|
||||
fun updateGuidedEnrollment(point: PointF, isRecreating: Boolean) {
|
||||
val pointIsZero = point.x == 0f && point.y == 0f
|
||||
val shouldAnimateMovement = pointIsZero || !isRecreating
|
||||
animateMovement(point?.x ?: 0f, point?.y ?: 0f, shouldAnimateMovement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -27,16 +27,15 @@ import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.Interpolator
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.graphics.toRectF
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.settings.R
|
||||
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sin
|
||||
@@ -145,7 +144,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
|
||||
/** Indicates enrollment progress has occurred. */
|
||||
fun onEnrollmentProgress(remaining: Int, totalSteps: Int, isRecreating: Boolean = false) {
|
||||
|
||||
afterFirstTouch = true
|
||||
updateProgress(remaining, totalSteps, isRecreating)
|
||||
}
|
||||
@@ -216,8 +214,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
* Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
|
||||
* respect to the parent framelayout.
|
||||
*/
|
||||
fun drawProgressAt(sensorRect: Rect) {
|
||||
this.sensorRect.set(sensorRect)
|
||||
fun drawProgressAt(overlayParams: UdfpsOverlayParams) {
|
||||
this.sensorRect.set(overlayParams.sensorBounds)
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
@@ -249,8 +247,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
restoreAnimationTime()
|
||||
}
|
||||
|
||||
this.remainingSteps = remainingSteps
|
||||
this.totalSteps = totalSteps
|
||||
this.remainingSteps = remainingSteps
|
||||
this.totalSteps = totalSteps
|
||||
val targetProgress = (totalSteps - remainingSteps).toFloat().div(max(1, totalSteps))
|
||||
@@ -290,12 +286,8 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
checkMarkDrawable.bounds = newBounds
|
||||
checkMarkDrawable.setVisible(true, false)
|
||||
}
|
||||
doOnEnd {
|
||||
onFinishedCompletionAnimation?.let{
|
||||
it()
|
||||
}
|
||||
doOnEnd { onFinishedCompletionAnimation?.let { it() } }
|
||||
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
@@ -356,6 +348,7 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
private fun flashHelpFillColor() {
|
||||
if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
|
||||
fillColorAnimator!!.end()
|
||||
fillColorAnimator = null
|
||||
}
|
||||
@ColorInt val targetColor = helpColor
|
||||
fillColorAnimator =
|
||||
@@ -375,7 +368,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
* want to re-animate the progress/success animation with the default timer
|
||||
*/
|
||||
private fun setAnimationTimeToZero() {
|
||||
fillColorAnimationDuration = 0
|
||||
animateArcDuration = 0
|
||||
checkmarkAnimationDelayDuration = 0
|
||||
checkmarkAnimationDuration = 0
|
||||
@@ -383,7 +375,6 @@ class UdfpsEnrollProgressBarDrawableV2(private val context: Context, attrs: Attr
|
||||
|
||||
/** This sets animation timers back to normal, this happens after we have */
|
||||
private fun restoreAnimationTime() {
|
||||
fillColorAnimationDuration = FILL_COLOR_ANIMATION_DURATION_MS
|
||||
animateArcDuration = PROGRESS_ANIMATION_DURATION_MS
|
||||
checkmarkAnimationDelayDuration = CHECKMARK_ANIMATION_DELAY_MS
|
||||
checkmarkAnimationDuration = CHECKMARK_ANIMATION_DURATION_MS
|
||||
|
||||
@@ -70,27 +70,11 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
fun setSensorRect(rect: Rect, sensorType: FingerprintSensorType) {
|
||||
this.sensorRect = rect
|
||||
this.fingerprintSensorType = sensorType
|
||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||
it.setImageDrawable(fingerprintProgressDrawable)
|
||||
}
|
||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||
it.setImageDrawable(fingerprintIcon)
|
||||
}
|
||||
|
||||
val rotation = display.rotation
|
||||
var displayInfo = DisplayInfo()
|
||||
context.display.getDisplayInfo(displayInfo)
|
||||
val rotation = displayInfo.rotation
|
||||
val scaleFactor = udfpsUtils.getScaleFactor(displayInfo)
|
||||
val overlayParams =
|
||||
UdfpsOverlayParams(
|
||||
sensorRect,
|
||||
fingerprintProgressDrawable.bounds,
|
||||
displayInfo.naturalWidth,
|
||||
displayInfo.naturalHeight,
|
||||
scaleFactor,
|
||||
rotation,
|
||||
sensorType.toInt(),
|
||||
)
|
||||
val parentView = parent as ViewGroup
|
||||
val coords = parentView.getLocationOnScreen()
|
||||
val parentLeft = coords[0]
|
||||
@@ -99,22 +83,44 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
// If the view has been rotated, we need to translate the sensor coordinates
|
||||
// to the new rotated view.
|
||||
when (rotation) {
|
||||
Surface.ROTATION_90,
|
||||
Surface.ROTATION_270 -> {
|
||||
Surface.ROTATION_90 -> {
|
||||
sensorRectOffset.set(
|
||||
sensorRectOffset.top,
|
||||
sensorRectOffset.left,
|
||||
sensorRectOffset.bottom,
|
||||
sensorRectOffset.right,
|
||||
)
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
// When the view is rotated 270 degrees, 0,0 is the top corner left
|
||||
Surface.ROTATION_270 -> {
|
||||
sensorRectOffset.set(
|
||||
(displayInfo.naturalHeight - sensorRectOffset.bottom) - parentLeft,
|
||||
sensorRectOffset.left - parentTop,
|
||||
(displayInfo.naturalHeight - sensorRectOffset.top) - parentLeft,
|
||||
sensorRectOffset.right - parentTop,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
||||
fingerprintIcon.drawSensorRectAt(sensorRectOffset)
|
||||
fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
|
||||
sensorRectOffset.offset(-parentLeft, -parentTop)
|
||||
}
|
||||
}
|
||||
|
||||
// Translate the sensor position into UdfpsEnrollView's view space.
|
||||
val overlayParams =
|
||||
UdfpsOverlayParams(
|
||||
sensorRectOffset,
|
||||
fingerprintProgressDrawable.bounds,
|
||||
displayInfo.naturalWidth,
|
||||
displayInfo.naturalHeight,
|
||||
scaleFactor,
|
||||
rotation,
|
||||
sensorType.toInt(),
|
||||
)
|
||||
|
||||
fingerprintIcon.drawSensorRectAt(overlayParams)
|
||||
fingerprintProgressDrawable.drawProgressAt(overlayParams)
|
||||
|
||||
touchExplorationAnnouncer = TouchExplorationAnnouncer(context, this, overlayParams, udfpsUtils)
|
||||
}
|
||||
@@ -126,11 +132,8 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
onEnrollmentProgress(event.remainingSteps, event.totalStepsRequired)
|
||||
is FingerEnrollState.Acquired -> onAcquired(event.acquiredGood)
|
||||
is FingerEnrollState.EnrollHelp -> onEnrollmentHelp()
|
||||
is FingerEnrollState.PointerDown -> onPointerDown()
|
||||
is FingerEnrollState.PointerUp -> onPointerUp()
|
||||
is FingerEnrollState.OverlayShown -> overlayShown()
|
||||
is FingerEnrollState.EnrollError ->
|
||||
throw IllegalArgumentException("$TAG should not handle udfps error")
|
||||
// Else ignore
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +148,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
}
|
||||
}
|
||||
|
||||
private fun udfpsError(errMsgId: Int, errString: String) {}
|
||||
/**
|
||||
* Sends a touch exploration event to the [onHoverListener] this should only be used for
|
||||
* debugging.
|
||||
@@ -170,8 +172,15 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
onHoverListener = listener
|
||||
}
|
||||
|
||||
private fun overlayShown() {
|
||||
Log.e(TAG, "Implement overlayShown")
|
||||
/** Indicates the overlay has been shown */
|
||||
fun overlayShown() {
|
||||
Log.d(TAG, "Showing udfps overlay")
|
||||
findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
|
||||
it.setImageDrawable(fingerprintProgressDrawable)
|
||||
}
|
||||
findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
|
||||
it.setImageDrawable(fingerprintIcon)
|
||||
}
|
||||
}
|
||||
|
||||
/** Receive enroll progress event */
|
||||
@@ -190,16 +199,6 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired()
|
||||
}
|
||||
|
||||
/** Receive onPointerDown event */
|
||||
private fun onPointerDown() {
|
||||
fingerprintIcon.stopDrawing()
|
||||
}
|
||||
|
||||
/** Receive onPointerUp event */
|
||||
private fun onPointerUp() {
|
||||
fingerprintIcon.startDrawing()
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
// Because the layout has changed, we need to recompute all locations.
|
||||
@@ -261,6 +260,17 @@ class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(co
|
||||
fingerprintIcon.updateGuidedEnrollment(point, false)
|
||||
}
|
||||
|
||||
/** Indicates if the enroll icon should be drawn. */
|
||||
fun shouldDrawIcon(it: Boolean) {
|
||||
post {
|
||||
if (it) {
|
||||
fingerprintIcon.startDrawing()
|
||||
} else {
|
||||
fingerprintIcon.stopDrawing()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UdfpsEnrollView"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.android.systemui.biometrics.shared.model.FingerprintSensor
|
||||
@@ -68,7 +69,8 @@ class FingerprintEnrollEnrollingViewModel(
|
||||
val enrollFlow =
|
||||
enrollFlowShouldBeRunning.transformLatest {
|
||||
if (it) {
|
||||
fingerprintEnrollViewModel.enrollFlow.collect { event -> emit(event) }
|
||||
fingerprintEnrollViewModel.enrollFlow.collect { event ->
|
||||
emit(event) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,4 +84,8 @@ class FingerprintEnrollEnrollingViewModel(
|
||||
as T
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "FingerprintEnrollEnrollingViewModel"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user