Merge "Coordinate circle reveal with authRipple" into sc-dev am: 603ade2fcc am: 43ef04ef59

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14982805

Change-Id: I7e26cba2d0fe72db02dbaec0570586b7aef78f5c
This commit is contained in:
TreeHugger Robot
2021-06-16 23:08:41 +00:00
committed by Automerger Merge Worker
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.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils 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.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry 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.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
@@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor(
private val commandRegistry: CommandRegistry, private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController, private val notificationShadeWindowController: NotificationShadeWindowController,
private val bypassController: KeyguardBypassController, private val bypassController: KeyguardBypassController,
private val biometricUnlockController: BiometricUnlockController,
rippleView: AuthRippleView? rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) { ) : ViewController<AuthRippleView>(rippleView) {
var fingerprintSensorLocation: PointF? = null var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null private var faceSensorLocation: PointF? = null
private var circleReveal: LightRevealEffect? = null
@VisibleForTesting @VisibleForTesting
public override fun onViewAttached() { public override fun onViewAttached() {
@@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor(
private fun showRipple() { private fun showRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this) notificationShadeWindowController.setForcePluginOpen(true, this)
mView.startRipple(Runnable { 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) notificationShadeWindowController.setForcePluginOpen(false, this)
}) if (useCircleReveal) {
lightRevealScrim?.revealEffect = LiftReveal
}
},
/* circleReveal */
if (useCircleReveal) {
lightRevealScrim
} else {
null
}
)
} }
fun updateSensorLocation() { fun updateSensorLocation() {
fingerprintSensorLocation = authController.fingerprintSensorLocation fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation 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() { private fun updateRippleColor() {

View File

@@ -31,6 +31,7 @@ import android.util.MathUtils
import android.view.View import android.view.View
import android.view.animation.PathInterpolator import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils import com.android.internal.graphics.ColorUtils
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_ANIMATION_DURATION: Long = 1533 private const val RIPPLE_ANIMATION_DURATION: Long = 1533
@@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
.toFloat() .toFloat()
} }
fun startRipple(onAnimationEnd: Runnable?) { fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
if (rippleInProgress) { if (rippleInProgress) {
return // Ignore if ripple effect is already playing return // Ignore if ripple effect is already playing
} }
val animator = ValueAnimator.ofFloat(0f, 1f) val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f) interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
animator.duration = RIPPLE_ANIMATION_DURATION duration = RIPPLE_ANIMATION_DURATION
animator.addUpdateListener { animator -> addUpdateListener { animator ->
val now = animator.currentPlayTime val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float rippleShader.progress = animator.animatedValue as Float
rippleShader.time = now.toFloat() rippleShader.time = now.toFloat()
rippleShader.distortionStrength = 1 - rippleShader.progress
lightReveal?.revealAmount = animator.animatedValue as Float
invalidate() 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 animatorSet = AnimatorSet() val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator) interpolator = rippleAnimator.interpolator
animatorSet.addListener(object : AnimatorListenerAdapter() { 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?) { override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd?.run() onAnimationEnd?.run()
rippleInProgress = false rippleInProgress = false
visibility = GONE visibility = GONE
} }
}) })
}
// TODO (b/185124905): custom haptic TBD // TODO (b/185124905): custom haptic TBD
// vibrate() // vibrate()
animatorSet.start() animatorSet.start()
visibility = VISIBLE
rippleInProgress = true
} }
fun setColor(color: Int) { fun setColor(color: Int) {

View File

@@ -93,10 +93,10 @@ class CircleReveal(
val endRadius: Float val endRadius: Float
) : LightRevealEffect { ) : LightRevealEffect {
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) { override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount) // reveal amount updates already have an interpolator, so we intentionally use the
val fadeAmount = // non-interpolated amount
LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f) val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount) val radius = startRadius + ((endRadius - startRadius) * amount)
scrim.revealGradientEndColorAlpha = 1f - fadeAmount scrim.revealGradientEndColorAlpha = 1f - fadeAmount
scrim.setRevealGradientBounds( scrim.setRevealGradientBounds(
centerX - radius /* left */, 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.WindowType;
import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString; 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.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType; import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; 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.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST; 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.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityOptions; import android.app.ActivityOptions;
@@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView; import android.widget.DateTimeView;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.LifecycleRegistry;
@@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private LightRevealScrim mLightRevealScrim; private LightRevealScrim mLightRevealScrim;
private WiredChargingRippleController mChargingRippleAnimationController; private WiredChargingRippleController mChargingRippleAnimationController;
private PowerButtonReveal mPowerButtonReveal; private PowerButtonReveal mPowerButtonReveal;
private CircleReveal mCircleReveal;
private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f);
private final Object mQueueLock = new Object(); private final Object mQueueLock = new Object();
@@ -2808,11 +2804,11 @@ public class StatusBar extends SystemUI implements DemoMode,
return mDisplayMetrics.density; return mDisplayMetrics.density;
} }
float getDisplayWidth() { public float getDisplayWidth() {
return mDisplayMetrics.widthPixels; return mDisplayMetrics.widthPixels;
} }
float getDisplayHeight() { public float getDisplayHeight() {
return mDisplayMetrics.heightPixels; return mDisplayMetrics.heightPixels;
} }
@@ -3542,9 +3538,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void fadeKeyguardWhilePulsing() { public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING, mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> { ()-> {
if (shouldShowCircleReveal()) {
startCircleReveal();
}
hideKeyguard(); hideKeyguard();
mStatusBarKeyguardViewManager.onKeyguardFadedAway(); mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}).start(); }).start();
@@ -3885,7 +3878,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override @Override
public void onDozeAmountChanged(float linear, float eased) { public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.useNewLockscreenAnimations() if (mFeatureFlags.useNewLockscreenAnimations()
&& !mCircleRevealAnimator.isRunning()) { && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear); mLightRevealScrim.setRevealAmount(1f - linear);
} }
} }
@@ -3908,7 +3901,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|| (!isDozing && mWakefulnessLifecycle.getLastWakeReason() || (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON)) { == PowerManager.WAKE_REASON_POWER_BUTTON)) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal); mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
} else if (!mCircleRevealAnimator.isRunning()) { } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
} }
@@ -3920,36 +3913,8 @@ public class StatusBar extends SystemUI implements DemoMode,
Trace.endSection(); Trace.endSection();
} }
/** public LightRevealScrim getLightRevealScrim() {
* Update the parameters for the dozing circle reveal that animates when the user authenticates return mLightRevealScrim;
* 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;
} }
private void updateKeyguardState() { private void updateKeyguardState() {

View File

@@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry 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.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var authController: AuthController @Mock private lateinit var authController: AuthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var bypassController: KeyguardBypassController @Mock private lateinit var bypassController: KeyguardBypassController
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Before @Before
fun setUp() { fun setUp() {
@@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
commandRegistry, commandRegistry,
notificationShadeWindowController, notificationShadeWindowController,
bypassController, bypassController,
biometricUnlockController,
rippleView rippleView
) )
controller.init() controller.init()
@@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN update sensor location and show ripple // THEN update sensor location and show ripple
verify(rippleView).setSensorLocation(fpsLocation) verify(rippleView).setSensorLocation(fpsLocation)
verify(rippleView).startRipple(any()) verify(rippleView).startRipple(any(), any())
} }
@Test @Test
@@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */) false /* isStrongBiometric */)
// THEN no ripple // THEN no ripple
verify(rippleView, never()).startRipple(any()) verify(rippleView, never()).startRipple(any(), any())
} }
@Test @Test
@@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */) false /* isStrongBiometric */)
// THEN no ripple // THEN no ripple
verify(rippleView, never()).startRipple(any()) verify(rippleView, never()).startRipple(any(), any())
} }
@Test @Test
@@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN show ripple // THEN show ripple
verify(rippleView).setSensorLocation(faceLocation) verify(rippleView).setSensorLocation(faceLocation)
verify(rippleView).startRipple(any()) verify(rippleView).startRipple(any(), any())
} }
@Test @Test
@@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */) false /* isStrongBiometric */)
// THEN no ripple // THEN no ripple
verify(rippleView, never()).startRipple(any()) verify(rippleView, never()).startRipple(any(), any())
} }
@Test @Test
@@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */, 0 /* userId */,
BiometricSourceType.FACE /* type */, BiometricSourceType.FACE /* type */,
false /* isStrongBiometric */) false /* isStrongBiometric */)
verify(rippleView, never()).startRipple(any()) verify(rippleView, never()).startRipple(any(), any())
} }
@Test @Test
@@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */, 0 /* userId */,
BiometricSourceType.FINGERPRINT /* type */, BiometricSourceType.FINGERPRINT /* type */,
false /* isStrongBiometric */) false /* isStrongBiometric */)
verify(rippleView, never()).startRipple(any()) verify(rippleView, never()).startRipple(any(), any())
} }
@Test @Test