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

Change-Id: I1467de52a7ad396153419e42b4413346e23edb78
This commit is contained in:
Automerger Merge Worker
2020-03-17 04:17:54 +00:00
2 changed files with 158 additions and 6 deletions

View File

@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.view.Choreographer
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -29,6 +30,7 @@ import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
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.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
@@ -45,7 +47,7 @@ import kotlin.math.max
*/
@Singleton
class NotificationShadeDepthController @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController,
private val statusBarStateController: StatusBarStateController,
private val blurUtils: BlurUtils,
private val biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
@@ -56,7 +58,6 @@ class NotificationShadeDepthController @Inject constructor(
) : PanelExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
private const val SHADE_BLUR_ENABLED = true
}
lateinit var root: View
@@ -64,7 +65,9 @@ class NotificationShadeDepthController @Inject constructor(
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
private val shadeSpring = SpringAnimation(this, object :
private var shadeExpansion = 0f
@VisibleForTesting
var shadeSpring = SpringAnimation(this, object :
FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
shadeBlurRadius = value.toInt()
@@ -75,12 +78,25 @@ class NotificationShadeDepthController @Inject constructor(
}
})
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
set(value) {
if (field == value) return
field = value
scheduleUpdate()
}
/**
* Blur radius of the wake-up animation on this frame.
*/
private var wakeAndUnlockBlurRadius = 0
set(value) {
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 {
dumpManager.registerDumpable(javaClass.name, this)
if (WAKE_UP_ANIMATION_ENABLED) {
@@ -149,24 +177,31 @@ class NotificationShadeDepthController @Inject constructor(
shadeSpring.spring = SpringForce(0.0f)
shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
statusBarStateController.addCallback(statusBarStateCallback)
}
/**
* Update blurs when pulling down the shade
*/
override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
if (!SHADE_BLUR_ENABLED) {
if (expansion == shadeExpansion) {
return
}
shadeExpansion = expansion
updateShadeBlur()
}
private fun updateShadeBlur() {
var newBlur = 0
if (statusBarStateController.state == StatusBarState.SHADE) {
newBlur = blurUtils.blurRadiusOfRatio(expansion)
newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
}
if (shadeBlurRadius == newBlur) {
if (pendingShadeBlurRadius == newBlur) {
return
}
pendingShadeBlurRadius = newBlur
shadeSpring.animateToFinalPosition(newBlur.toFloat())
}

View File

@@ -0,0 +1,117 @@
/*
* 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
}
}