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

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

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

Merge "Updated blur interpolation for notification shade" into rvc-dev am: 9b35a417 am: 0ae8ae52

Change-Id: I016ca0432f62355fc0d1c3644b6f9f6b8ee1d34d
parents 14f51764 0ae8ae52
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 {