Merge "On UDFPS auth success, fade out the dwell ripple" into tm-dev

This commit is contained in:
Beverly Tai 2022-03-31 13:52:38 +00:00 committed by Android (Google) Code Review
commit a545b2b7b4
4 changed files with 82 additions and 25 deletions

View File

@ -652,7 +652,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
// pre-emptively set to true to hide view
mIsBouncerShowing = true;
if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
mAuthRippleController.showRipple(FINGERPRINT);
mAuthRippleController.showUnlockRipple(FINGERPRINT);
}
updateVisibility();
if (mOnGestureDetectedRunnable != null) {

View File

@ -52,7 +52,11 @@ import javax.inject.Inject
import javax.inject.Provider
/***
* Controls the ripple effect that shows when authentication is successful.
* Controls two ripple effects:
* 1. Unlocked ripple: shows when authentication is successful
* 2. UDFPS dwell ripple: shows when the user has their finger down on the UDFPS area and reacts
* to errors and successes
*
* The ripple uses the accent color of the current theme.
*/
@CentralSurfacesScope
@ -115,7 +119,7 @@ class AuthRippleController @Inject constructor(
notificationShadeWindowController.setForcePluginOpen(false, this)
}
fun showRipple(biometricSourceType: BiometricSourceType?) {
fun showUnlockRipple(biometricSourceType: BiometricSourceType?) {
if (!(keyguardUpdateMonitor.isKeyguardVisible || keyguardUpdateMonitor.isDreaming) ||
keyguardUpdateMonitor.userNeedsStrongAuth()) {
return
@ -252,11 +256,16 @@ class AuthRippleController @Inject constructor(
biometricSourceType: BiometricSourceType?,
isStrongBiometric: Boolean
) {
showRipple(biometricSourceType)
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.fadeDwellRipple()
}
showUnlockRipple(biometricSourceType)
}
override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
mView.retractRipple()
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.retractDwellRipple()
}
}
override fun onBiometricAcquired(
@ -264,8 +273,16 @@ class AuthRippleController @Inject constructor(
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
acquireInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL) {
mView.retractRipple()
BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) &&
acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
// received an 'acquiredBad' message, so immediately retract
mView.retractDwellRipple()
}
}
override fun onKeyguardBouncerChanged(bouncerIsOrWillBeShowing: Boolean) {
if (bouncerIsOrWillBeShowing) {
mView.fadeDwellRipple()
}
}
}
@ -294,7 +311,7 @@ class AuthRippleController @Inject constructor(
}
override fun onFingerUp() {
mView.retractRipple()
mView.retractDwellRipple()
}
}
@ -337,12 +354,12 @@ class AuthRippleController @Inject constructor(
"fingerprint" -> {
updateSensorLocation()
pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
showRipple(BiometricSourceType.FINGERPRINT)
showUnlockRipple(BiometricSourceType.FINGERPRINT)
}
"face" -> {
updateSensorLocation()
pw.println("face ripple sensorLocation=$faceSensorLocation")
showRipple(BiometricSourceType.FACE)
showUnlockRipple(BiometricSourceType.FACE)
}
"custom" -> {
if (args.size != 3 ||

View File

@ -35,12 +35,13 @@ import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
/**
* Expanding ripple effect
* - startUnlockedRipple for the transition from biometric authentication success to showing
* launcher.
* - startDwellRipple for the ripple expansion out when the user has their finger down on the UDFPS
* sensor area
* - retractRipple for the ripple animation inwards to signal a failure
* Handles two ripple effects: dwell ripple and unlocked ripple
* Dwell Ripple:
* - startDwellRipple: dwell ripple expands outwards around the biometric area
* - retractDwellRipple: retracts the dwell ripple to radius 0 to signal a failure
* - fadeDwellRipple: fades the dwell ripple away to alpha 0
* Unlocked ripple:
* - startUnlockedRipple: ripple expands from biometric auth location to the edges of the screen
*/
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
@ -52,6 +53,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
private var drawRipple: Boolean = false
private var lockScreenColorVal = Color.WHITE
private val fadeDuration = 83L
private val retractDuration = 400L
private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false
@ -59,7 +61,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
private val dwellPaint = Paint()
private val rippleShader = RippleShader()
private val ripplePaint = Paint()
private var retractAnimator: Animator? = null
private var fadeDwellAnimator: Animator? = null
private var retractDwellAnimator: Animator? = null
private var dwellPulseOutAnimator: Animator? = null
private var dwellRadius: Float = 0f
set(value) {
@ -112,15 +115,15 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
/**
* Animate ripple inwards back to radius 0
* Animate dwell ripple inwards back to radius 0
*/
fun retractRipple() {
if (retractAnimator?.isRunning == true) {
fun retractDwellRipple() {
if (retractDwellAnimator?.isRunning == true || fadeDwellAnimator?.isRunning == true) {
return // let the animation finish
}
if (dwellPulseOutAnimator?.isRunning == true) {
val retractRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
val retractDwellRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
.apply {
interpolator = retractInterpolator
duration = retractDuration
@ -145,8 +148,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
}
retractAnimator = AnimatorSet().apply {
playTogether(retractRippleAnimator, retractAlphaAnimator)
retractDwellAnimator = AnimatorSet().apply {
playTogether(retractDwellRippleAnimator, retractAlphaAnimator)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
dwellPulseOutAnimator?.cancel()
@ -163,6 +166,42 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
}
/**
* Animate ripple fade to alpha=0
*/
fun fadeDwellRipple() {
if (fadeDwellAnimator?.isRunning == true) {
return // let the animation finish
}
if (dwellPulseOutAnimator?.isRunning == true || retractDwellAnimator?.isRunning == true) {
fadeDwellAnimator = ValueAnimator.ofInt(Color.alpha(dwellShader.color), 0).apply {
interpolator = Interpolators.LINEAR
duration = fadeDuration
addUpdateListener { animator ->
dwellShader.color = ColorUtils.setAlphaComponent(
dwellShader.color,
animator.animatedValue as Int
)
invalidate()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
retractDwellAnimator?.cancel()
dwellPulseOutAnimator?.cancel()
drawDwell = true
}
override fun onAnimationEnd(animation: Animator?) {
drawDwell = false
resetDwellAlpha()
}
})
start()
}
}
}
/**
* Plays a ripple animation that grows to the dwellRadius with distortion.
*/
@ -205,7 +244,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
retractAnimator?.cancel()
retractDwellAnimator?.cancel()
fadeDwellAnimator?.cancel()
visibility = VISIBLE
drawDwell = true
}

View File

@ -299,7 +299,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
`when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
controller.showRipple(BiometricSourceType.FINGERPRINT)
controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
assertTrue("reveal didn't start on keyguardFadingAway",
controller.startLightRevealScrimOnKeyguardFadingAway)
`when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)