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
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 // pre-emptively set to true to hide view
mIsBouncerShowing = true; mIsBouncerShowing = true;
if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) { if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
mAuthRippleController.showRipple(FINGERPRINT); mAuthRippleController.showUnlockRipple(FINGERPRINT);
} }
updateVisibility(); updateVisibility();
if (mOnGestureDetectedRunnable != null) { if (mOnGestureDetectedRunnable != null) {

View File

@@ -52,7 +52,11 @@ import javax.inject.Inject
import javax.inject.Provider 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. * The ripple uses the accent color of the current theme.
*/ */
@CentralSurfacesScope @CentralSurfacesScope
@@ -115,7 +119,7 @@ class AuthRippleController @Inject constructor(
notificationShadeWindowController.setForcePluginOpen(false, this) notificationShadeWindowController.setForcePluginOpen(false, this)
} }
fun showRipple(biometricSourceType: BiometricSourceType?) { fun showUnlockRipple(biometricSourceType: BiometricSourceType?) {
if (!(keyguardUpdateMonitor.isKeyguardVisible || keyguardUpdateMonitor.isDreaming) || if (!(keyguardUpdateMonitor.isKeyguardVisible || keyguardUpdateMonitor.isDreaming) ||
keyguardUpdateMonitor.userNeedsStrongAuth()) { keyguardUpdateMonitor.userNeedsStrongAuth()) {
return return
@@ -252,11 +256,16 @@ class AuthRippleController @Inject constructor(
biometricSourceType: BiometricSourceType?, biometricSourceType: BiometricSourceType?,
isStrongBiometric: Boolean isStrongBiometric: Boolean
) { ) {
showRipple(biometricSourceType) if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.fadeDwellRipple()
}
showUnlockRipple(biometricSourceType)
} }
override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) { override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
mView.retractRipple() if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.retractDwellRipple()
}
} }
override fun onBiometricAcquired( override fun onBiometricAcquired(
@@ -264,8 +273,16 @@ class AuthRippleController @Inject constructor(
acquireInfo: Int acquireInfo: Int
) { ) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT && if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
acquireInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL) { BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) &&
mView.retractRipple() 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() { override fun onFingerUp() {
mView.retractRipple() mView.retractDwellRipple()
} }
} }
@@ -337,12 +354,12 @@ class AuthRippleController @Inject constructor(
"fingerprint" -> { "fingerprint" -> {
updateSensorLocation() updateSensorLocation()
pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation") pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
showRipple(BiometricSourceType.FINGERPRINT) showUnlockRipple(BiometricSourceType.FINGERPRINT)
} }
"face" -> { "face" -> {
updateSensorLocation() updateSensorLocation()
pw.println("face ripple sensorLocation=$faceSensorLocation") pw.println("face ripple sensorLocation=$faceSensorLocation")
showRipple(BiometricSourceType.FACE) showUnlockRipple(BiometricSourceType.FACE)
} }
"custom" -> { "custom" -> {
if (args.size != 3 || 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 private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
/** /**
* Expanding ripple effect * Handles two ripple effects: dwell ripple and unlocked ripple
* - startUnlockedRipple for the transition from biometric authentication success to showing * Dwell Ripple:
* launcher. * - startDwellRipple: dwell ripple expands outwards around the biometric area
* - startDwellRipple for the ripple expansion out when the user has their finger down on the UDFPS * - retractDwellRipple: retracts the dwell ripple to radius 0 to signal a failure
* sensor area * - fadeDwellRipple: fades the dwell ripple away to alpha 0
* - retractRipple for the ripple animation inwards to signal a failure * Unlocked ripple:
* - startUnlockedRipple: ripple expands from biometric auth location to the edges of the screen
*/ */
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f) 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 drawRipple: Boolean = false
private var lockScreenColorVal = Color.WHITE private var lockScreenColorVal = Color.WHITE
private val fadeDuration = 83L
private val retractDuration = 400L private val retractDuration = 400L
private var alphaInDuration: Long = 0 private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false private var unlockedRippleInProgress: Boolean = false
@@ -59,7 +61,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
private val dwellPaint = Paint() private val dwellPaint = Paint()
private val rippleShader = RippleShader() private val rippleShader = RippleShader()
private val ripplePaint = Paint() 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 dwellPulseOutAnimator: Animator? = null
private var dwellRadius: Float = 0f private var dwellRadius: Float = 0f
set(value) { 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() { fun retractDwellRipple() {
if (retractAnimator?.isRunning == true) { if (retractDwellAnimator?.isRunning == true || fadeDwellAnimator?.isRunning == true) {
return // let the animation finish return // let the animation finish
} }
if (dwellPulseOutAnimator?.isRunning == true) { if (dwellPulseOutAnimator?.isRunning == true) {
val retractRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f) val retractDwellRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
.apply { .apply {
interpolator = retractInterpolator interpolator = retractInterpolator
duration = retractDuration duration = retractDuration
@@ -145,8 +148,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
} }
} }
retractAnimator = AnimatorSet().apply { retractDwellAnimator = AnimatorSet().apply {
playTogether(retractRippleAnimator, retractAlphaAnimator) playTogether(retractDwellRippleAnimator, retractAlphaAnimator)
addListener(object : AnimatorListenerAdapter() { addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) { override fun onAnimationStart(animation: Animator?) {
dwellPulseOutAnimator?.cancel() 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. * 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() { addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) { override fun onAnimationStart(animation: Animator?) {
retractAnimator?.cancel() retractDwellAnimator?.cancel()
fadeDwellAnimator?.cancel()
visibility = VISIBLE visibility = VISIBLE
drawDwell = true drawDwell = true
} }

View File

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