Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca058eac authored by Lucas Dupin's avatar Lucas Dupin Committed by Automerger Merge Worker
Browse files

Merge "Revealing the light reveal scrim only during keyguard exit animation"...

Merge "Revealing the light reveal scrim only during keyguard exit animation" into sc-dev am: 5837f7b4

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

Change-Id: Ib9876302e6ee64427f30a085e482ce92186db0f0
parents f81b0b3d 5837f7b4
Loading
Loading
Loading
Loading
+50 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.biometrics

import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.graphics.PointF
@@ -26,6 +27,8 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -36,12 +39,15 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
import com.android.systemui.plugins.statusbar.StatusBarStateController

private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L

/***
 * Controls the ripple effect that shows when authentication is successful.
 * The ripple uses the accent color of the current theme.
@@ -53,6 +59,8 @@ class AuthRippleController @Inject constructor(
    private val authController: AuthController,
    private val configurationController: ConfigurationController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val keyguardStateController: KeyguardStateController,
    private val wakefulnessLifecycle: WakefulnessLifecycle,
    private val commandRegistry: CommandRegistry,
    private val notificationShadeWindowController: NotificationShadeWindowController,
    private val bypassController: KeyguardBypassController,
@@ -60,7 +68,11 @@ class AuthRippleController @Inject constructor(
    private val udfpsControllerProvider: Provider<UdfpsController>,
    private val statusBarStateController: StatusBarStateController,
    rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
    WakefulnessLifecycle.Observer {

    @VisibleForTesting
    internal var startLightRevealScrimOnKeyguardFadingAway = false
    var fingerprintSensorLocation: PointF? = null
    private var faceSensorLocation: PointF? = null
    private var circleReveal: LightRevealEffect? = null
@@ -87,6 +99,8 @@ class AuthRippleController @Inject constructor(
        udfpsController?.addCallback(udfpsControllerCallback)
        configurationController.addCallback(configurationChangedListener)
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        keyguardStateController.addCallback(this)
        wakefulnessLifecycle.addObserver(this)
        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
    }

@@ -96,6 +110,8 @@ class AuthRippleController @Inject constructor(
        authController.removeCallback(authControllerCallback)
        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        configurationController.removeCallback(configurationChangedListener)
        keyguardStateController.removeCallback(this)
        wakefulnessLifecycle.removeObserver(this)
        commandRegistry.unregisterCommand("auth-ripple")

        notificationShadeWindowController.setForcePluginOpen(false, this)
@@ -123,30 +139,48 @@ class AuthRippleController @Inject constructor(

    private fun showUnlockedRipple() {
        notificationShadeWindowController.setForcePluginOpen(true, 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 useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
        val lightRevealScrim = statusBar.lightRevealScrim
        if (useCircleReveal) {
            lightRevealScrim?.revealEffect = circleReveal!!
            startLightRevealScrimOnKeyguardFadingAway = true
        }

        mView.startUnlockedRipple(
            /* end runnable */
            Runnable {
                notificationShadeWindowController.setForcePluginOpen(false, this)
            },
            /* circleReveal */
            if (useCircleReveal) {
                lightRevealScrim
            } else {
                null
            }
        )
    }

    override fun onKeyguardFadingAwayChanged() {
        if (keyguardStateController.isKeyguardFadingAway) {
            val lightRevealScrim = statusBar.lightRevealScrim
            if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
                val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
                    interpolator = Interpolators.LINEAR_OUT_SLOW_IN
                    duration = RIPPLE_ANIMATION_DURATION
                    startDelay = keyguardStateController.keyguardFadingAwayDelay
                    addUpdateListener { animator ->
                        if (lightRevealScrim.revealEffect != circleReveal) {
                            // if the something else took over the reveal, let's do nothing.
                            return@addUpdateListener
                        }
                        lightRevealScrim.revealAmount = animator.animatedValue as Float
                    }
                }
                revealAnimator.start()
                startLightRevealScrimOnKeyguardFadingAway = false
            }
        }
    }

    override fun onStartedGoingToSleep() {
        // reset the light reveal start in case we were pending an unlock
        startLightRevealScrimOnKeyguardFadingAway = false
    }

    fun updateSensorLocation() {
        fingerprintSensorLocation = authController.fingerprintSensorLocation
        faceSensorLocation = authController.faceAuthSensorLocation
@@ -318,4 +352,8 @@ class AuthRippleController @Inject constructor(
            help(pw)
        }
    }

    companion object {
        const val RIPPLE_ANIMATION_DURATION: Long = 1533
    }
}
+2 −14
Original line number Diff line number Diff line
@@ -26,13 +26,10 @@ import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.R.attr.interpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader

private const val RIPPLE_ANIMATION_DURATION: Long = 1533
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f

/**
@@ -250,7 +247,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
    /**
     * Ripple that bursts outwards from the position of the sensor to the edges of the screen
     */
    fun startUnlockedRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
    fun startUnlockedRipple(onAnimationEnd: Runnable?) {
        if (unlockedRippleInProgress) {
            return // Ignore if ripple effect is already playing
        }
@@ -266,7 +263,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at

        val rippleAnimator = ValueAnimator.ofFloat(rippleStart, 1f).apply {
            interpolator = Interpolators.LINEAR_OUT_SLOW_IN
            duration = RIPPLE_ANIMATION_DURATION
            duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
            addUpdateListener { animator ->
                val now = animator.currentPlayTime
                rippleShader.progress = animator.animatedValue as Float
@@ -276,14 +273,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
            }
        }

        val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
            interpolator = rippleAnimator.interpolator
            duration = rippleAnimator.duration
            addUpdateListener { animator ->
                lightReveal?.revealAmount = animator.animatedValue as Float
            }
        }

        val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
            duration = alphaDuration
            addUpdateListener { animator ->
@@ -298,7 +287,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        val animatorSet = AnimatorSet().apply {
            playTogether(
                rippleAnimator,
                revealAnimator,
                alphaInAnimator
            )
            addListener(object : AnimatorListenerAdapter() {
+14 −3
Original line number Diff line number Diff line
@@ -818,6 +818,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,

    private final KeyguardStateController mKeyguardStateController;
    private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
    private boolean mWallpaperSupportsAmbientMode;

    /**
     * Injected constructor. See {@link KeyguardModule}.
@@ -2089,13 +2090,14 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,

            int flags = 0;
            if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
                    || (mWakeAndUnlocking && !mPulsing)
                    || isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
                    || mWakeAndUnlocking && !mWallpaperSupportsAmbientMode) {
                flags |= WindowManagerPolicyConstants
                        .KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
            }
            if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade()
                    || (mWakeAndUnlocking && mPulsing)) {
                    || mWakeAndUnlocking && mWallpaperSupportsAmbientMode) {
                // When the wallpaper supports ambient mode, the scrim isn't fully opaque during
                // wake and unlock and we should fade in the app on top of the wallpaper
                flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
            }
            if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
@@ -2784,6 +2786,15 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
        mPulsing = pulsing;
    }

    /**
     * Set if the wallpaper supports ambient mode. This is used to trigger the right animation.
     * In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
     * with the light reveal scrim.
     */
    public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
        mWallpaperSupportsAmbientMode = supportsAmbientMode;
    }

    private static class StartKeyguardExitAnimParams {

        @WindowManager.TransitionOldType int mTransit;
+3 −1
Original line number Diff line number Diff line
@@ -592,6 +592,7 @@ public class StatusBar extends SystemUI implements DemoMode,

            mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
            mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
            mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
        }
    };

@@ -3913,7 +3914,8 @@ public class StatusBar extends SystemUI implements DemoMode,
    @Override
    public void onDozeAmountChanged(float linear, float eased) {
        if (mFeatureFlags.useNewLockscreenAnimations()
                && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
                && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
                && !mBiometricUnlockController.isWakeAndUnlock()) {
            mLightRevealScrim.setRevealAmount(1f - linear);
        }
    }
+51 −7
Original line number Diff line number Diff line
@@ -19,17 +19,23 @@ package com.android.systemui.biometrics
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
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
import com.android.systemui.statusbar.policy.KeyguardStateController
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,12 +61,15 @@ class AuthRippleControllerTest : SysuiTestCase() {
    @Mock private lateinit var configurationController: ConfigurationController
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var authController: AuthController
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
    @Mock private lateinit var bypassController: KeyguardBypassController
    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
    @Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
    @Mock private lateinit var udfpsController: UdfpsController
    @Mock private lateinit var statusBarStateController: StatusBarStateController
    @Mock private lateinit var lightRevealScrim: LightRevealScrim

    @Before
    fun setUp() {
@@ -73,6 +82,8 @@ class AuthRippleControllerTest : SysuiTestCase() {
            authController,
            configurationController,
            keyguardUpdateMonitor,
            keyguardStateController,
            wakefulnessLifecycle,
            commandRegistry,
            notificationShadeWindowController,
            bypassController,
@@ -82,6 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            rippleView
        )
        controller.init()
        `when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
    }

    @Test
@@ -103,7 +115,7 @@ class AuthRippleControllerTest : SysuiTestCase() {

        // THEN update sensor location and show ripple
        verify(rippleView).setSensorLocation(fpsLocation)
        verify(rippleView).startUnlockedRipple(any(), any())
        verify(rippleView).startUnlockedRipple(any())
    }

    @Test
@@ -124,7 +136,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startUnlockedRipple(any(), any())
        verify(rippleView, never()).startUnlockedRipple(any())
    }

    @Test
@@ -145,7 +157,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startUnlockedRipple(any(), any())
        verify(rippleView, never()).startUnlockedRipple(any())
    }

    @Test
@@ -169,7 +181,7 @@ class AuthRippleControllerTest : SysuiTestCase() {

        // THEN show ripple
        verify(rippleView).setSensorLocation(faceLocation)
        verify(rippleView).startUnlockedRipple(any(), any())
        verify(rippleView).startUnlockedRipple(any())
    }

    @Test
@@ -189,7 +201,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN no ripple
        verify(rippleView, never()).startUnlockedRipple(any(), any())
        verify(rippleView, never()).startUnlockedRipple(any())
    }

    @Test
@@ -204,7 +216,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            0 /* userId */,
            BiometricSourceType.FACE /* type */,
            false /* isStrongBiometric */)
        verify(rippleView, never()).startUnlockedRipple(any(), any())
        verify(rippleView, never()).startUnlockedRipple(any())
    }

    @Test
@@ -219,7 +231,39 @@ class AuthRippleControllerTest : SysuiTestCase() {
            0 /* userId */,
            BiometricSourceType.FINGERPRINT /* type */,
            false /* isStrongBiometric */)
        verify(rippleView, never()).startUnlockedRipple(any(), any())
        verify(rippleView, never()).startUnlockedRipple(any())
    }

    @Test
    fun registersAndDeregisters() {
        controller.onViewAttached()
        val captor = ArgumentCaptor
            .forClass(KeyguardStateController.Callback::class.java)
        verify(keyguardStateController).addCallback(captor.capture())
        val captor2 = ArgumentCaptor
            .forClass(WakefulnessLifecycle.Observer::class.java)
        verify(wakefulnessLifecycle).addObserver(captor2.capture())
        controller.onViewDetached()
        verify(keyguardStateController).removeCallback(any())
        verify(wakefulnessLifecycle).removeObserver(any())
    }

    @Test
    @RunWithLooper(setAsMainLooper = true)
    fun testAnimatorRunWhenWakeAndUnlock() {
        val fpsLocation = PointF(5f, 5f)
        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
        controller.onViewAttached()
        `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
        `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)

        controller.showRipple(BiometricSourceType.FINGERPRINT)
        assertTrue("reveal didn't start on keyguardFadingAway",
            controller.startLightRevealScrimOnKeyguardFadingAway)
        `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
        controller.onKeyguardFadingAwayChanged()
        assertFalse("reveal triggers multiple times",
            controller.startLightRevealScrimOnKeyguardFadingAway)
    }

    @Test