Loading packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +26 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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( Loading @@ -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() } } } Loading Loading @@ -294,7 +311,7 @@ class AuthRippleController @Inject constructor( } override fun onFingerUp() { mView.retractRipple() mView.retractDwellRipple() } } Loading Loading @@ -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 || Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +54 −14 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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() Loading @@ -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. */ Loading Loading @@ -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 } Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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) Loading Loading
packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +26 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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( Loading @@ -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() } } } Loading Loading @@ -294,7 +311,7 @@ class AuthRippleController @Inject constructor( } override fun onFingerUp() { mView.retractRipple() mView.retractDwellRipple() } } Loading Loading @@ -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 || Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +54 −14 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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() Loading @@ -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. */ Loading Loading @@ -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 } Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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) Loading