Merge "[conflict] Merge "Fix BP face auth requiring full tap after auth" into udc-d1-dev am: d97c897215" into udc-qpr-dev
This commit is contained in:
@@ -646,8 +646,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
shouldPilfer = true;
|
||||
}
|
||||
|
||||
// Pilfer only once per gesture
|
||||
if (shouldPilfer && !mPointerPilfered) {
|
||||
// Pilfer only once per gesture, don't pilfer for BP
|
||||
if (shouldPilfer && !mPointerPilfered
|
||||
&& getBiometricSessionType() != SESSION_BIOMETRIC_PROMPT) {
|
||||
mInputManager.pilferPointers(
|
||||
mOverlay.getOverlayView().getViewRootImpl().getInputToken());
|
||||
mPointerPilfered = true;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.biometrics.ui.binder
|
||||
|
||||
import android.animation.Animator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.hardware.biometrics.BiometricAuthenticator
|
||||
import android.hardware.biometrics.BiometricConstants
|
||||
@@ -26,6 +27,7 @@ import android.os.Bundle
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.util.Log
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
@@ -73,6 +75,7 @@ private const val TAG = "BiometricViewBinder"
|
||||
object BiometricViewBinder {
|
||||
|
||||
/** Binds a [BiometricPromptLayout] to a [PromptViewModel]. */
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: BiometricPromptLayout,
|
||||
@@ -300,21 +303,19 @@ object BiometricViewBinder {
|
||||
|
||||
// reuse the icon as a confirm button
|
||||
launch {
|
||||
viewModel.isConfirmButtonVisible
|
||||
viewModel.isIconConfirmButton
|
||||
.map { isPending ->
|
||||
when {
|
||||
isPending && iconController.actsAsConfirmButton ->
|
||||
View.OnClickListener { viewModel.confirmAuthenticated() }
|
||||
View.OnTouchListener { _: View, event: MotionEvent ->
|
||||
viewModel.onOverlayTouch(event)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.collect { onClick ->
|
||||
iconViewOverlay.setOnClickListener(onClick)
|
||||
iconView.setOnClickListener(onClick)
|
||||
if (onClick == null) {
|
||||
iconViewOverlay.isClickable = false
|
||||
iconView.isClickable = false
|
||||
}
|
||||
.collect { onTouch ->
|
||||
iconViewOverlay.setOnTouchListener(onTouch)
|
||||
iconView.setOnTouchListener(onTouch)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +341,14 @@ object BiometricViewBinder {
|
||||
backgroundView.setOnClickListener(null)
|
||||
backgroundView.importantForAccessibility =
|
||||
IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
|
||||
// Allow icon to be used as confirmation button with a11y enabled
|
||||
if (accessibilityManager.isTouchExplorationEnabled) {
|
||||
iconViewOverlay.setOnClickListener {
|
||||
viewModel.confirmAuthenticated()
|
||||
}
|
||||
iconView.setOnClickListener { viewModel.confirmAuthenticated() }
|
||||
}
|
||||
}
|
||||
if (authState.isAuthenticatedAndConfirmed) {
|
||||
view.announceForAccessibility(
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel
|
||||
import android.hardware.biometrics.BiometricPrompt
|
||||
import android.util.Log
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import com.android.systemui.biometrics.AuthBiometricView
|
||||
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
|
||||
import com.android.systemui.biometrics.domain.model.BiometricModalities
|
||||
@@ -67,11 +68,18 @@ constructor(
|
||||
/** If the user has successfully authenticated and confirmed (when explicitly required). */
|
||||
val isAuthenticated: Flow<PromptAuthState> = _isAuthenticated.asStateFlow()
|
||||
|
||||
private val _isOverlayTouched: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
|
||||
/**
|
||||
* If the API caller or the user's personal preferences require explicit confirmation after
|
||||
* successful authentication.
|
||||
*/
|
||||
val isConfirmationRequired: Flow<Boolean> = interactor.isConfirmationRequired
|
||||
val isConfirmationRequired: Flow<Boolean> =
|
||||
combine(_isOverlayTouched, interactor.isConfirmationRequired) {
|
||||
isOverlayTouched,
|
||||
isConfirmationRequired ->
|
||||
!isOverlayTouched && isConfirmationRequired
|
||||
}
|
||||
|
||||
/** The kind of credential the user has. */
|
||||
val credentialKind: Flow<PromptKind> = interactor.credentialKind
|
||||
@@ -150,6 +158,12 @@ constructor(
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
/** If the icon can be used as a confirmation button. */
|
||||
val isIconConfirmButton: Flow<Boolean> =
|
||||
combine(size, interactor.isConfirmationRequired) { size, isConfirmationRequired ->
|
||||
size.isNotSmall && isConfirmationRequired
|
||||
}
|
||||
|
||||
/** If the negative button should be shown. */
|
||||
val isNegativeButtonVisible: Flow<Boolean> =
|
||||
combine(
|
||||
@@ -298,8 +312,10 @@ constructor(
|
||||
if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty
|
||||
_forceMediumSize.value = true
|
||||
_legacyState.value =
|
||||
if (alreadyAuthenticated) {
|
||||
if (alreadyAuthenticated && isConfirmationRequired.first()) {
|
||||
AuthBiometricView.STATE_PENDING_CONFIRMATION
|
||||
} else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
|
||||
AuthBiometricView.STATE_AUTHENTICATED
|
||||
} else {
|
||||
AuthBiometricView.STATE_HELP
|
||||
}
|
||||
@@ -397,18 +413,10 @@ constructor(
|
||||
}
|
||||
|
||||
private suspend fun needsExplicitConfirmation(modality: BiometricModality): Boolean {
|
||||
val availableModalities = modalities.first()
|
||||
val confirmationRequired = isConfirmationRequired.first()
|
||||
|
||||
if (availableModalities.hasFaceAndFingerprint) {
|
||||
// coex only needs confirmation when face is successful, unless it happens on the
|
||||
// first attempt (i.e. without failure) before fingerprint scanning starts
|
||||
val fingerprintStarted = fingerprintStartMode.first() != FingerprintStartMode.Pending
|
||||
if (modality == BiometricModality.Face) {
|
||||
return fingerprintStarted || confirmationRequired
|
||||
}
|
||||
}
|
||||
if (availableModalities.hasFaceOnly) {
|
||||
// Only worry about confirmationRequired if face was used to unlock
|
||||
if (modality == BiometricModality.Face) {
|
||||
return confirmationRequired
|
||||
}
|
||||
// fingerprint only never requires confirmation
|
||||
@@ -438,6 +446,26 @@ constructor(
|
||||
messageJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch event occurred on the overlay
|
||||
*
|
||||
* Tracks whether a finger is currently down to set [_isOverlayTouched] to be used as user
|
||||
* confirmation
|
||||
*/
|
||||
fun onOverlayTouch(event: MotionEvent): Boolean {
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
_isOverlayTouched.value = true
|
||||
|
||||
if (_isAuthenticated.value.needsUserConfirmation) {
|
||||
confirmAuthenticated()
|
||||
}
|
||||
return true
|
||||
} else if (event.actionMasked == MotionEvent.ACTION_UP) {
|
||||
_isOverlayTouched.value = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the credential view.
|
||||
*
|
||||
|
||||
@@ -527,6 +527,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
|
||||
val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
|
||||
val size by collectLastValue(viewModel.size)
|
||||
val legacyState by collectLastValue(viewModel.legacyState)
|
||||
val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)
|
||||
|
||||
if (testCase.isCoex && testCase.authenticatedByFingerprint) {
|
||||
viewModel.ensureFingerprintHasStarted(isDelayed = true)
|
||||
@@ -535,7 +536,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
|
||||
viewModel.showHelp(helpMessage)
|
||||
|
||||
assertThat(size).isEqualTo(PromptSize.MEDIUM)
|
||||
assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
|
||||
if (confirmationRequired == true) {
|
||||
assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
|
||||
} else {
|
||||
assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED)
|
||||
}
|
||||
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
|
||||
assertThat(messageVisible).isTrue()
|
||||
assertThat(authenticating).isFalse()
|
||||
|
||||
Reference in New Issue
Block a user