diff options
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 e67c0814f4f6..f152d0cc28fd 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(); @@ -2808,11 +2804,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; } @@ -3542,9 +3538,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(); @@ -3885,7 +3878,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); } } @@ -3908,7 +3901,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); } @@ -3920,36 +3913,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 |