diff options
| author | 2021-06-15 11:56:13 -0400 | |
|---|---|---|
| committer | 2021-06-15 15:36:01 -0400 | |
| commit | 28a01ca4587a65c51c8f7f1c0a25179d1ff724c3 (patch) | |
| tree | 87b40d8a662d511ff0bd4c9886ab8da427ba65a0 | |
| parent | c45de70925dcfa36bbcaa0d131e9e786b68c7a68 (diff) | |
Coordinate circle reveal with authRipple
Move CircleReveal from StatusBar to AuthRippleController so we
can coordinate the light reveal scrim with the auth ripple when
the user uses fingerprint to authenticate on AOD.
Test: manual
Fixes: 190617092
Change-Id: If210cebb801ca0f66405401710bac6087a313eea
5 files changed, 125 insertions, 90 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index cf577a37d625..77cca2e3089c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -24,9 +24,13 @@ import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.settingslib.Utils +import com.android.systemui.statusbar.CircleReveal +import com.android.systemui.statusbar.LiftReveal +import com.android.systemui.statusbar.LightRevealEffect import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope @@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor( private val commandRegistry: CommandRegistry, private val notificationShadeWindowController: NotificationShadeWindowController, private val bypassController: KeyguardBypassController, + private val biometricUnlockController: BiometricUnlockController, rippleView: AuthRippleView? ) : ViewController<AuthRippleView>(rippleView) { var fingerprintSensorLocation: PointF? = null private var faceSensorLocation: PointF? = null + private var circleReveal: LightRevealEffect? = null @VisibleForTesting public override fun onViewAttached() { @@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor( private fun showRipple() { notificationShadeWindowController.setForcePluginOpen(true, this) - mView.startRipple(Runnable { - notificationShadeWindowController.setForcePluginOpen(false, this) - }) + val biometricUnlockMode = biometricUnlockController.mode + val useCircleReveal = circleReveal != null && + (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK || + biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING || + biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) + val lightRevealScrim = statusBar.lightRevealScrim + if (useCircleReveal) { + lightRevealScrim?.revealEffect = circleReveal!! + } + + mView.startRipple( + /* end runnable */ + Runnable { + notificationShadeWindowController.setForcePluginOpen(false, this) + if (useCircleReveal) { + lightRevealScrim?.revealEffect = LiftReveal + } + }, + /* circleReveal */ + if (useCircleReveal) { + lightRevealScrim + } else { + null + } + ) } fun updateSensorLocation() { fingerprintSensorLocation = authController.fingerprintSensorLocation faceSensorLocation = authController.faceAuthSensorLocation - statusBar.updateCircleReveal() + fingerprintSensorLocation?.let { + circleReveal = CircleReveal( + it.x, + it.y, + 0f, + Math.max( + Math.max(it.x, statusBar.displayWidth - it.x), + Math.max(it.y, statusBar.displayHeight - it.y) + ) + ) + } } private fun updateRippleColor() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index 75373abc5124..dd73c4f8d071 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -31,6 +31,7 @@ import android.util.MathUtils import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils +import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.charging.RippleShader private const val RIPPLE_ANIMATION_DURATION: Long = 1533 @@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at .toFloat() } - fun startRipple(onAnimationEnd: Runnable?) { + fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) { if (rippleInProgress) { return // Ignore if ripple effect is already playing } - val animator = ValueAnimator.ofFloat(0f, 1f) - animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) - animator.duration = RIPPLE_ANIMATION_DURATION - animator.addUpdateListener { animator -> - val now = animator.currentPlayTime - rippleShader.progress = animator.animatedValue as Float - rippleShader.time = now.toFloat() - rippleShader.distortionStrength = 1 - rippleShader.progress - invalidate() + val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) + duration = RIPPLE_ANIMATION_DURATION + addUpdateListener { animator -> + val now = animator.currentPlayTime + rippleShader.progress = animator.animatedValue as Float + rippleShader.time = now.toFloat() + + lightReveal?.revealAmount = animator.animatedValue as Float + invalidate() + } } - val alphaInAnimator = ValueAnimator.ofInt(0, 127) - alphaInAnimator.duration = 167 - alphaInAnimator.addUpdateListener { alphaInAnimator -> - rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color, - alphaInAnimator.animatedValue as Int) - invalidate() + + val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + interpolator = rippleAnimator.interpolator + startDelay = 10 + duration = rippleAnimator.duration + addUpdateListener { animator -> + lightReveal?.revealAmount = animator.animatedValue as Float + } } - val alphaOutAnimator = ValueAnimator.ofInt(127, 0) - alphaOutAnimator.startDelay = 417 - alphaOutAnimator.duration = 1116 - alphaOutAnimator.addUpdateListener { alphaOutAnimator -> - rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color, - alphaOutAnimator.animatedValue as Int) - invalidate() + + val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply { + duration = 167 + addUpdateListener { animator -> + rippleShader.color = ColorUtils.setAlphaComponent( + rippleShader.color, + animator.animatedValue as Int + ) + invalidate() + } } - val animatorSet = AnimatorSet() - animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator) - animatorSet.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - onAnimationEnd?.run() - rippleInProgress = false - visibility = GONE + val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply { + startDelay = 417 + duration = 1116 + addUpdateListener { animator -> + rippleShader.color = ColorUtils.setAlphaComponent( + rippleShader.color, + animator.animatedValue as Int + ) + invalidate() } - }) + } + + val animatorSet = AnimatorSet().apply { + playTogether( + rippleAnimator, + revealAnimator, + alphaInAnimator, + alphaOutAnimator + ) + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?) { + rippleInProgress = true + visibility = VISIBLE + } + + override fun onAnimationEnd(animation: Animator?) { + onAnimationEnd?.run() + rippleInProgress = false + visibility = GONE + } + }) + } // TODO (b/185124905): custom haptic TBD // vibrate() animatorSet.start() - visibility = VISIBLE - rippleInProgress = true } fun setColor(color: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 6a5f001ac2ee..ec648ad519a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -93,10 +93,10 @@ class CircleReveal( val endRadius: Float ) : LightRevealEffect { override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) { - val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount) - val fadeAmount = - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f) - val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount) + // reveal amount updates already have an interpolator, so we intentionally use the + // non-interpolated amount + val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f) + val radius = startRadius + ((endRadius - startRadius) * amount) scrim.revealGradientEndColorAlpha = 1f - fadeAmount scrim.setRevealGradientBounds( centerX - radius /* left */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 5ee5e489479d..59e5ff53b012 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; -import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; @@ -46,8 +45,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST; -import android.animation.ValueAnimator; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.widget.DateTimeView; +import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode, private LightRevealScrim mLightRevealScrim; private WiredChargingRippleController mChargingRippleAnimationController; private PowerButtonReveal mPowerButtonReveal; - private CircleReveal mCircleReveal; - private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f); private final Object mQueueLock = new Object(); @@ -2803,11 +2799,11 @@ public class StatusBar extends SystemUI implements DemoMode, return mDisplayMetrics.density; } - float getDisplayWidth() { + public float getDisplayWidth() { return mDisplayMetrics.widthPixels; } - float getDisplayHeight() { + public float getDisplayHeight() { return mDisplayMetrics.heightPixels; } @@ -3513,9 +3509,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void fadeKeyguardWhilePulsing() { mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING, ()-> { - if (shouldShowCircleReveal()) { - startCircleReveal(); - } hideKeyguard(); mStatusBarKeyguardViewManager.onKeyguardFadedAway(); }).start(); @@ -3856,7 +3849,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onDozeAmountChanged(float linear, float eased) { if (mFeatureFlags.useNewLockscreenAnimations() - && !mCircleRevealAnimator.isRunning()) { + && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealAmount(1f - linear); } } @@ -3879,7 +3872,7 @@ public class StatusBar extends SystemUI implements DemoMode, || (!isDozing && mWakefulnessLifecycle.getLastWakeReason() == PowerManager.WAKE_REASON_POWER_BUTTON)) { mLightRevealScrim.setRevealEffect(mPowerButtonReveal); - } else if (!mCircleRevealAnimator.isRunning()) { + } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); } @@ -3891,36 +3884,8 @@ public class StatusBar extends SystemUI implements DemoMode, Trace.endSection(); } - /** - * Update the parameters for the dozing circle reveal that animates when the user authenticates - * from AOD using the fingerprint sensor. - */ - public void updateCircleReveal() { - final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation(); - if (fpLocation != null) { - mCircleReveal = - new CircleReveal( - fpLocation.x, - fpLocation.y, - 0, - Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x), - Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y))); - } - } - - private void startCircleReveal() { - mLightRevealScrim.setRevealEffect(mCircleReveal); - mCircleRevealAnimator.cancel(); - mCircleRevealAnimator.addUpdateListener(animation -> - mLightRevealScrim.setRevealAmount( - (float) mCircleRevealAnimator.getAnimatedValue())); - mCircleRevealAnimator.setDuration(900); - mCircleRevealAnimator.start(); - } - - private boolean shouldShowCircleReveal() { - return mCircleReveal != null && !mCircleRevealAnimator.isRunning() - && mBiometricUnlockController.getBiometricType() == FINGERPRINT; + public LightRevealScrim getLightRevealScrim() { + return mLightRevealScrim; } private void updateKeyguardState() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 240fdf3a4e17..d87a26b096fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.policy.ConfigurationController @@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() { @Mock private lateinit var authController: AuthController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var bypassController: KeyguardBypassController + @Mock private lateinit var biometricUnlockController: BiometricUnlockController @Before fun setUp() { @@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() { commandRegistry, notificationShadeWindowController, bypassController, + biometricUnlockController, rippleView ) controller.init() @@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() { // THEN update sensor location and show ripple verify(rippleView).setSensorLocation(fpsLocation) - verify(rippleView).startRipple(any()) + verify(rippleView).startRipple(any(), any()) } @Test @@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() { // THEN show ripple verify(rippleView).setSensorLocation(faceLocation) - verify(rippleView).startRipple(any()) + verify(rippleView).startRipple(any(), any()) } @Test @@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() { false /* isStrongBiometric */) // THEN no ripple - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() { 0 /* userId */, BiometricSourceType.FACE /* type */, false /* isStrongBiometric */) - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test @@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() { 0 /* userId */, BiometricSourceType.FINGERPRINT /* type */, false /* isStrongBiometric */) - verify(rippleView, never()).startRipple(any()) + verify(rippleView, never()).startRipple(any(), any()) } @Test |