Merge "Coordinate circle reveal with authRipple" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-06-16 22:42:56 +00:00
committed by Android (Google) Code Review
5 changed files with 127 additions and 92 deletions

View File

@@ -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() {

View File

@@ -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 alphaInAnimator = ValueAnimator.ofInt(0, 127)
alphaInAnimator.duration = 167
alphaInAnimator.addUpdateListener { alphaInAnimator ->
rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
alphaInAnimator.animatedValue as Int)
invalidate()
}
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 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 animatorSet = AnimatorSet()
animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd?.run()
rippleInProgress = false
visibility = GONE
val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
interpolator = rippleAnimator.interpolator
startDelay = 10
duration = rippleAnimator.duration
addUpdateListener { animator ->
lightReveal?.revealAmount = animator.animatedValue as Float
}
})
}
val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
duration = 167
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
animator.animatedValue as Int
)
invalidate()
}
}
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) {

View File

@@ -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 */,

View File

@@ -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() {

View File

@@ -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