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

Commit 9b35a417 authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Updated blur interpolation for notification shade" into rvc-dev

parents eaee42c4 bde7292c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -38,8 +38,8 @@ open class BlurUtils @Inject constructor(
    @Main private val resources: Resources,
    dumpManager: DumpManager
) : Dumpable {
    private val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
    private val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
    val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
    val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
    private val blurSupportedSysProp = SystemProperties
            .getBoolean("ro.surface_flinger.supports_background_blur", false)
    private val blurDisabledSysProp = SystemProperties
+141 −8
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.os.SystemClock
import android.util.Log
import android.util.MathUtils
import android.view.Choreographer
import android.view.View
import androidx.annotation.VisibleForTesting
@@ -44,6 +46,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.max
import kotlin.math.sign

/**
 * Controller responsible for statusbar window blur.
@@ -61,6 +64,11 @@ class NotificationShadeDepthController @Inject constructor(
) : PanelExpansionListener, Dumpable {
    companion object {
        private const val WAKE_UP_ANIMATION_ENABLED = true
        private const val VELOCITY_SCALE = 100f
        private const val MAX_VELOCITY = 3000f
        private const val MIN_VELOCITY = -MAX_VELOCITY
        private const val INTERACTION_BLUR_FRACTION = 0.4f
        private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION
        private const val TAG = "DepthController"
    }

@@ -71,8 +79,19 @@ class NotificationShadeDepthController @Inject constructor(
    private var updateScheduled: Boolean = false
    private var shadeExpansion = 0f
    private var ignoreShadeBlurUntilHidden: Boolean = false
    private var isClosed: Boolean = true
    private var isOpen: Boolean = false
    private var isBlurred: Boolean = false

    private var prevTracking: Boolean = false
    private var prevTimestamp: Long = -1
    private var prevShadeDirection = 0
    private var prevShadeVelocity = 0f

    @VisibleForTesting
    var shadeSpring = DepthAnimation()
    var shadeAnimation = DepthAnimation()

    @VisibleForTesting
    var globalActionsSpring = DepthAnimation()
    var showingHomeControls: Boolean = false
@@ -98,12 +117,15 @@ class NotificationShadeDepthController @Inject constructor(
                return
            }

            if (shadeSpring.radius == 0) {
            if (shadeSpring.radius == 0 && shadeAnimation.radius == 0) {
                return
            }
            ignoreShadeBlurUntilHidden = true
            shadeSpring.animateTo(0)
            shadeSpring.finishIfRunning()

            shadeAnimation.animateTo(0)
            shadeAnimation.finishIfRunning()
        }

    /**
@@ -132,8 +154,11 @@ class NotificationShadeDepthController @Inject constructor(
    @VisibleForTesting
    val updateBlurCallback = Choreographer.FrameCallback {
        updateScheduled = false

        var shadeRadius = max(shadeSpring.radius, wakeAndUnlockBlurRadius).toFloat()
        val normalizedBlurRadius = MathUtils.constrain(shadeAnimation.radius,
                blurUtils.minBlurRadius, blurUtils.maxBlurRadius)
        val combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
                normalizedBlurRadius * ANIMATION_BLUR_FRACTION).toInt()
        var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat()
        shadeRadius *= 1f - brightnessMirrorSpring.ratio
        val launchProgress = notificationLaunchAnimationParams?.linearProgress ?: 0f
        shadeRadius *= (1f - launchProgress) * (1f - launchProgress)
@@ -208,12 +233,15 @@ class NotificationShadeDepthController @Inject constructor(

    private val statusBarStateCallback = object : StatusBarStateController.StateListener {
        override fun onStateChanged(newState: Int) {
            updateShadeAnimationBlur(
                    shadeExpansion, prevTracking, prevShadeVelocity, prevShadeDirection)
            updateShadeBlur()
        }

        override fun onDozingChanged(isDozing: Boolean) {
            if (isDozing) {
                shadeSpring.finishIfRunning()
                shadeAnimation.finishIfRunning()
                globalActionsSpring.finishIfRunning()
                brightnessMirrorSpring.finishIfRunning()
            }
@@ -234,24 +262,110 @@ class NotificationShadeDepthController @Inject constructor(
            // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition.
            visibility -> scrimsVisible = visibility == ScrimController.OPAQUE
        }
        shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
        shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
    }

    /**
     * Update blurs when pulling down the shade
     */
    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
        if (expansion == shadeExpansion) {
        val timestamp = SystemClock.elapsedRealtimeNanos()

        if (shadeExpansion == expansion && prevTracking == tracking) {
            prevTimestamp = timestamp
            return
        }

        var deltaTime = 1f
        if (prevTimestamp < 0) {
            prevTimestamp = timestamp
        } else {
            deltaTime = MathUtils.constrain(
                    ((timestamp - prevTimestamp) / 1E9).toFloat(), 0.00001f, 1f)
        }

        val diff = expansion - shadeExpansion
        val shadeDirection = sign(diff).toInt()
        val shadeVelocity = MathUtils.constrain(
            VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY)
        updateShadeAnimationBlur(expansion, tracking, shadeVelocity, shadeDirection)

        prevShadeDirection = shadeDirection
        prevShadeVelocity = shadeVelocity
        shadeExpansion = expansion
        prevTracking = tracking
        prevTimestamp = timestamp

        updateShadeBlur()
    }

    private fun updateShadeAnimationBlur(
        expansion: Float,
        tracking: Boolean,
        velocity: Float,
        direction: Int
    ) {
        if (isOnKeyguardNotDismissing()) {
            if (expansion > 0f) {
                // Blur view if user starts animating in the shade.
                if (isClosed) {
                    animateBlur(true, velocity)
                    isClosed = false
                }

                // If we were blurring out and the user stopped the animation, blur view.
                if (tracking && !isBlurred) {
                    animateBlur(true, 0f)
                }

                // If shade is being closed and the user isn't interacting with it, un-blur.
                if (!tracking && direction < 0 && isBlurred) {
                    animateBlur(false, velocity)
                }

                if (expansion == 1f) {
                    if (!isOpen) {
                        isOpen = true
                        // If shade is open and view is not blurred, blur.
                        if (!isBlurred) {
                            animateBlur(true, velocity)
                        }
                    }
                } else {
                    isOpen = false
                }
                // Automatic animation when the user closes the shade.
            } else if (!isClosed) {
                isClosed = true
                // If shade is closed and view is not blurred, blur.
                if (isBlurred) {
                    animateBlur(false, velocity)
                }
            }
        } else {
            animateBlur(false, 0f)
            isClosed = true
            isOpen = false
        }
    }

    private fun animateBlur(blur: Boolean, velocity: Float) {
        isBlurred = blur

        val targetBlurNormalized = if (blur && isOnKeyguardNotDismissing()) {
            1f
        } else {
            0f
        }

        shadeAnimation.setStartVelocity(velocity)
        shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized))
    }

    private fun updateShadeBlur() {
        var newBlur = 0
        val state = statusBarStateController.state
        if ((state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) &&
                !keyguardStateController.isKeyguardFadingAway) {
        if (isOnKeyguardNotDismissing()) {
            newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
        }
        shadeSpring.animateTo(newBlur)
@@ -266,6 +380,12 @@ class NotificationShadeDepthController @Inject constructor(
        choreographer.postFrameCallback(updateBlurCallback)
    }

    private fun isOnKeyguardNotDismissing(): Boolean {
        val state = statusBarStateController.state
        return (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) &&
                !keyguardStateController.isKeyguardFadingAway
    }

    fun updateGlobalDialogVisibility(visibility: Float, dialogView: View?) {
        globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView)
    }
@@ -275,6 +395,7 @@ class NotificationShadeDepthController @Inject constructor(
            it.println("StatusBarWindowBlurController:")
            it.increaseIndent()
            it.println("shadeRadius: ${shadeSpring.radius}")
            it.println("shadeAnimation: ${shadeAnimation.radius}")
            it.println("globalActionsRadius: ${globalActionsSpring.radius}")
            it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
            it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
@@ -343,5 +464,17 @@ class NotificationShadeDepthController @Inject constructor(
                springAnimation.skipToEnd()
            }
        }

        fun setStiffness(stiffness: Float) {
            springAnimation.spring.stiffness = stiffness
        }

        fun setDampingRatio(dampingRation: Float) {
            springAnimation.spring.dampingRatio = dampingRation
        }

        fun setStartVelocity(velocity: Float) {
            springAnimation.setStartVelocity(velocity)
        }
    }
}
+55 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
    @Mock private lateinit var root: View
    @Mock private lateinit var viewRootImpl: ViewRootImpl
    @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
    @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
    @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
    @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -80,11 +81,15 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
        `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
            (answer.arguments[0] as Float * maxBlur).toInt()
        }
        `when`(blurUtils.minBlurRadius).thenReturn(0)
        `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)

        notificationShadeDepthController = NotificationShadeDepthController(
                statusBarStateController, blurUtils, biometricUnlockController,
                keyguardStateController, choreographer, wallpaperManager,
                notificationShadeWindowController, dumpManager)
        notificationShadeDepthController.shadeSpring = shadeSpring
        notificationShadeDepthController.shadeAnimation = shadeAnimation
        notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
        notificationShadeDepthController.globalActionsSpring = globalActionsSpring
        notificationShadeDepthController.root = root
@@ -104,16 +109,61 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
        notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                false /* tracking */)
        verify(shadeSpring).animateTo(eq(maxBlur), any())
        verify(shadeAnimation).animateTo(eq(maxBlur), any())
    }

    @Test
    fun onPanelExpansionChanged_animatesBlurIn_ifShade() {
        notificationShadeDepthController.onPanelExpansionChanged(0.01f /* expansion */,
                false /* tracking */)
        verify(shadeAnimation).animateTo(eq(maxBlur), any())
    }

    @Test
    fun onPanelExpansionChanged_animatesBlurOut_ifShade() {
        onPanelExpansionChanged_animatesBlurIn_ifShade()
        clearInvocations(shadeAnimation)
        notificationShadeDepthController.onPanelExpansionChanged(0f /* expansion */,
                false /* tracking */)
        verify(shadeAnimation).animateTo(eq(0), any())
    }

    @Test
    fun onPanelExpansionChanged_animatesBlurOut_ifFlick() {
        onPanelExpansionChanged_apliesBlur_ifShade()
        clearInvocations(shadeAnimation)
        notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                true /* tracking */)
        verify(shadeAnimation, never()).animateTo(anyInt(), any())

        notificationShadeDepthController.onPanelExpansionChanged(0.9f /* expansion */,
                true /* tracking */)
        verify(shadeAnimation, never()).animateTo(anyInt(), any())

        notificationShadeDepthController.onPanelExpansionChanged(0.8f /* expansion */,
                false /* tracking */)
        verify(shadeAnimation).animateTo(eq(0), any())
    }

    @Test
    fun onPanelExpansionChanged_animatesBlurIn_ifFlickCancelled() {
        onPanelExpansionChanged_animatesBlurOut_ifFlick()
        clearInvocations(shadeAnimation)
        notificationShadeDepthController.onPanelExpansionChanged(0.6f /* expansion */,
                true /* tracking */)
        verify(shadeAnimation).animateTo(eq(maxBlur), any())
    }

    @Test
    fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() {
        onPanelExpansionChanged_apliesBlur_ifShade()
        clearInvocations(shadeSpring)
        clearInvocations(shadeAnimation)

        statusBarState = StatusBarState.KEYGUARD
        statusBarStateListener.onStateChanged(statusBarState)
        verify(shadeSpring).animateTo(eq(0), any())
        verify(shadeAnimation).animateTo(eq(0), any())
    }

    @Test
@@ -147,6 +197,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
    @Test
    fun updateBlurCallback_setsBlur_whenExpanded() {
        `when`(shadeSpring.radius).thenReturn(maxBlur)
        `when`(shadeAnimation.radius).thenReturn(maxBlur)
        notificationShadeDepthController.updateBlurCallback.doFrame(0)
        verify(blurUtils).applyBlur(any(), eq(maxBlur))
    }
@@ -154,6 +205,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
    @Test
    fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
        `when`(shadeSpring.radius).thenReturn(maxBlur)
        `when`(shadeAnimation.radius).thenReturn(maxBlur)
        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
        animProgress.linearProgress = 1f
        notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
@@ -187,6 +239,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
        `when`(brightnessSpring.ratio).thenReturn(1f)
        // And shade is blurred
        `when`(shadeSpring.radius).thenReturn(maxBlur)
        `when`(shadeAnimation.radius).thenReturn(maxBlur)

        notificationShadeDepthController.updateBlurCallback.doFrame(0)
        verify(notificationShadeWindowController).setBackgroundBlurRadius(0)
@@ -207,8 +260,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
        animProgress.linearProgress = 0.5f
        `when`(shadeSpring.radius).thenReturn(0)
        `when`(shadeAnimation.radius).thenReturn(0)
        notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
        verify(shadeSpring, never()).animateTo(anyInt(), any())
        verify(shadeAnimation, never()).animateTo(anyInt(), any())
    }

    private fun <T : Any> safeEq(value: T): T {