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

Commit 17fe1902 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Fix issue where blurs would get stuck" into rvc-dev am: 13dcbd32 am:...

Merge "Fix issue where blurs would get stuck" into rvc-dev am: 13dcbd32 am: ff71e5a6 am: 5683a3e1

Change-Id: I70ff411161629bc9faa24a1fe7ccb5042c3ed426
parents 650ae7eb 5683a3e1
Loading
Loading
Loading
Loading
+41 −6
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.app.WallpaperManager
import android.view.Choreographer
import android.view.Choreographer
import android.view.View
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import androidx.dynamicanimation.animation.SpringForce
@@ -29,6 +30,7 @@ import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
import com.android.systemui.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
@@ -45,7 +47,7 @@ import kotlin.math.max
 */
 */
@Singleton
@Singleton
class NotificationShadeDepthController @Inject constructor(
class NotificationShadeDepthController @Inject constructor(
    private val statusBarStateController: SysuiStatusBarStateController,
    private val statusBarStateController: StatusBarStateController,
    private val blurUtils: BlurUtils,
    private val blurUtils: BlurUtils,
    private val biometricUnlockController: BiometricUnlockController,
    private val biometricUnlockController: BiometricUnlockController,
    private val keyguardStateController: KeyguardStateController,
    private val keyguardStateController: KeyguardStateController,
@@ -56,7 +58,6 @@ class NotificationShadeDepthController @Inject constructor(
) : PanelExpansionListener, Dumpable {
) : PanelExpansionListener, Dumpable {
    companion object {
    companion object {
        private const val WAKE_UP_ANIMATION_ENABLED = true
        private const val WAKE_UP_ANIMATION_ENABLED = true
        private const val SHADE_BLUR_ENABLED = true
    }
    }


    lateinit var root: View
    lateinit var root: View
@@ -64,7 +65,9 @@ class NotificationShadeDepthController @Inject constructor(
    private var keyguardAnimator: Animator? = null
    private var keyguardAnimator: Animator? = null
    private var notificationAnimator: Animator? = null
    private var notificationAnimator: Animator? = null
    private var updateScheduled: Boolean = false
    private var updateScheduled: Boolean = false
    private val shadeSpring = SpringAnimation(this, object :
    private var shadeExpansion = 0f
    @VisibleForTesting
    var shadeSpring = SpringAnimation(this, object :
            FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
            FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
        override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
        override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
            shadeBlurRadius = value.toInt()
            shadeBlurRadius = value.toInt()
@@ -75,12 +78,25 @@ class NotificationShadeDepthController @Inject constructor(
        }
        }
    })
    })
    private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
    private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE

    /**
     * Radius that we're animating to.
     */
    private var pendingShadeBlurRadius = -1

    /**
     * Shade blur radius on the current frame.
     */
    private var shadeBlurRadius = 0
    private var shadeBlurRadius = 0
        set(value) {
        set(value) {
            if (field == value) return
            if (field == value) return
            field = value
            field = value
            scheduleUpdate()
            scheduleUpdate()
        }
        }

    /**
     * Blur radius of the wake-up animation on this frame.
     */
    private var wakeAndUnlockBlurRadius = 0
    private var wakeAndUnlockBlurRadius = 0
        set(value) {
        set(value) {
            if (field == value) return
            if (field == value) return
@@ -141,6 +157,18 @@ class NotificationShadeDepthController @Inject constructor(
        }
        }
    }
    }


    private val statusBarStateCallback = object : StatusBarStateController.StateListener {
        override fun onStateChanged(newState: Int) {
            updateShadeBlur()
        }

        override fun onDozingChanged(isDozing: Boolean) {
            if (isDozing && shadeSpring.isRunning) {
                shadeSpring.skipToEnd()
            }
        }
    }

    init {
    init {
        dumpManager.registerDumpable(javaClass.name, this)
        dumpManager.registerDumpable(javaClass.name, this)
        if (WAKE_UP_ANIMATION_ENABLED) {
        if (WAKE_UP_ANIMATION_ENABLED) {
@@ -149,24 +177,31 @@ class NotificationShadeDepthController @Inject constructor(
        shadeSpring.spring = SpringForce(0.0f)
        shadeSpring.spring = SpringForce(0.0f)
        shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
        shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
        shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
        shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
        shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
        statusBarStateController.addCallback(statusBarStateCallback)
    }
    }


    /**
    /**
     * Update blurs when pulling down the shade
     * Update blurs when pulling down the shade
     */
     */
    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
        if (!SHADE_BLUR_ENABLED) {
        if (expansion == shadeExpansion) {
            return
            return
        }
        }
        shadeExpansion = expansion
        updateShadeBlur()
    }


    private fun updateShadeBlur() {
        var newBlur = 0
        var newBlur = 0
        if (statusBarStateController.state == StatusBarState.SHADE) {
        if (statusBarStateController.state == StatusBarState.SHADE) {
            newBlur = blurUtils.blurRadiusOfRatio(expansion)
            newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
        }
        }


        if (shadeBlurRadius == newBlur) {
        if (pendingShadeBlurRadius == newBlur) {
            return
            return
        }
        }
        pendingShadeBlurRadius = newBlur
        shadeSpring.animateToFinalPosition(newBlur.toFloat())
        shadeSpring.animateToFinalPosition(newBlur.toFloat())
    }
    }


+117 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar

import android.app.WallpaperManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Choreographer
import android.view.View
import android.view.ViewRootImpl
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.policy.KeyguardStateController
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnit

@RunWith(AndroidTestingRunner::class)
@RunWithLooper
@SmallTest
class NotificationShadeDepthControllerTest : SysuiTestCase() {

    @Mock private lateinit var statusBarStateController: StatusBarStateController
    @Mock private lateinit var blurUtils: BlurUtils
    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var choreographer: Choreographer
    @Mock private lateinit var wallpaperManager: WallpaperManager
    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
    @Mock private lateinit var dumpManager: DumpManager
    @Mock private lateinit var root: View
    @Mock private lateinit var viewRootImpl: ViewRootImpl
    @Mock private lateinit var shadeSpring: SpringAnimation
    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()

    private lateinit var statusBarStateListener: StatusBarStateController.StateListener
    private var statusBarState = StatusBarState.SHADE
    private val maxBlur = 150
    private lateinit var notificationShadeDepthController: NotificationShadeDepthController

    @Before
    fun setup() {
        `when`(root.viewRootImpl).thenReturn(viewRootImpl)
        `when`(statusBarStateController.state).then { statusBarState }
        `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
            (answer.arguments[0] as Float * maxBlur).toInt()
        }
        notificationShadeDepthController = NotificationShadeDepthController(
                statusBarStateController, blurUtils, biometricUnlockController,
                keyguardStateController, choreographer, wallpaperManager,
                notificationShadeWindowController, dumpManager)
        notificationShadeDepthController.shadeSpring = shadeSpring
        notificationShadeDepthController.root = root

        val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
        verify(statusBarStateController).addCallback(captor.capture())
        statusBarStateListener = captor.value
    }

    @Test
    fun setupListeners() {
        verify(dumpManager).registerDumpable(anyString(), safeEq(notificationShadeDepthController))
    }

    @Test
    fun onPanelExpansionChanged_apliesBlur_ifShade() {
        notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                false /* tracking */)
        verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
    }

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

        statusBarState = StatusBarState.KEYGUARD
        statusBarStateListener.onStateChanged(statusBarState)
        verify(shadeSpring).animateToFinalPosition(eq(0f))
    }

    @Test
    fun updateGlobalDialogVisibility_schedulesUpdate() {
        notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
        verify(choreographer).postFrameCallback(any())
    }

    private fun <T : Any> safeEq(value: T): T {
        return eq(value) ?: value
    }
}
 No newline at end of file