Merge "Fix issue where blurs would get stuck" into rvc-dev am: 13dcbd321a
Change-Id: I1467de52a7ad396153419e42b4413346e23edb78
This commit is contained in:
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user