diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 395e946cf6db6..704c906911bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -16,15 +16,17 @@ package com.android.systemui.statusbar.notification -import android.animation.ObjectAnimator import android.util.FloatProperty import android.view.animation.Interpolator import androidx.annotation.VisibleForTesting +import androidx.core.animation.ObjectAnimator import com.android.systemui.Dumpable import com.android.systemui.animation.Interpolators +import com.android.systemui.animation.InterpolatorsAndroidX import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.NotificationPanelViewController.WAKEUP_ANIMATION_DELAY_MS import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener import com.android.systemui.statusbar.StatusBarState @@ -37,8 +39,11 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassSta import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener +import com.android.systemui.util.doOnCancel +import com.android.systemui.util.doOnEnd import java.io.PrintWriter import javax.inject.Inject +import kotlin.math.max import kotlin.math.min @SysUISingleton @@ -57,23 +62,13 @@ constructor( StatusBarStateController.StateListener, ShadeExpansionListener, Dumpable { - - private val mNotificationVisibility = - object : FloatProperty("notificationVisibility") { - - override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { - coordinator.setVisibilityAmount(value) - } - - override fun get(coordinator: NotificationWakeUpCoordinator): Float? { - return coordinator.mLinearVisibilityAmount - } - } private lateinit var mStackScrollerController: NotificationStackScrollLayoutController private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE private var inputLinearDozeAmount: Float = 0.0f private var inputEasedDozeAmount: Float = 0.0f + private var delayedDozeAmountOverride: Float = 0.0f + private var delayedDozeAmountAnimator: ObjectAnimator? = null /** Valid values: {1f, 0f, null} null => use input */ private var hardDozeAmountOverride: Float? = null private var hardDozeAmountOverrideSource: String = "n/a" @@ -319,7 +314,8 @@ constructor( private fun updateDozeAmount() { // Calculate new doze amount (linear) - val newOutputLinearDozeAmount = hardDozeAmountOverride ?: inputLinearDozeAmount + val newOutputLinearDozeAmount = + hardDozeAmountOverride ?: max(inputLinearDozeAmount, delayedDozeAmountOverride) val changed = outputLinearDozeAmount != newOutputLinearDozeAmount // notify when the animation is starting @@ -337,6 +333,7 @@ constructor( outputEasedDozeAmount = dozeAmountInterpolator.getInterpolation(outputLinearDozeAmount) logger.logUpdateDozeAmount( inputLinear = inputLinearDozeAmount, + delayLinear = delayedDozeAmountOverride, hardOverride = hardDozeAmountOverride, outputLinear = outputLinearDozeAmount, state = statusBarStateController.state, @@ -365,8 +362,27 @@ constructor( wakingUp: Boolean, requestDelayedAnimation: Boolean, ) { + logger.logSetWakingUp(wakingUp, requestDelayedAnimation) this.wakingUp = wakingUp - // TODO(jeffdq): Delay the wakeup animation if we're pressing to unlock. + if (wakingUp && requestDelayedAnimation) { + scheduleDelayedDozeAmountAnimation() + } + } + + private fun scheduleDelayedDozeAmountAnimation() { + val alreadyRunning = delayedDozeAmountAnimator != null + logger.logStartDelayedDozeAmountAnimation(alreadyRunning) + if (alreadyRunning) return + delayedDozeAmount.setValue(this, 1.0f) + delayedDozeAmountAnimator = + ObjectAnimator.ofFloat(this, delayedDozeAmount, 0.0f).apply { + interpolator = InterpolatorsAndroidX.LINEAR + duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() + startDelay = WAKEUP_ANIMATION_DELAY_MS.toLong() + doOnEnd { delayedDozeAmountAnimator = null } + doOnCancel { delayedDozeAmountAnimator = null } + start() + } } override fun onStateChanged(newState: Int) { @@ -489,8 +505,8 @@ constructor( else Interpolators.FAST_OUT_SLOW_IN_REVERSE } val target = if (mNotificationsVisible) 1.0f else 0.0f - val visibilityAnimator = ObjectAnimator.ofFloat(this, mNotificationVisibility, target) - visibilityAnimator.interpolator = Interpolators.LINEAR + val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target) + visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() if (increaseSpeed) { duration = (duration.toFloat() / 1.5F).toLong() @@ -501,6 +517,7 @@ constructor( } private fun setVisibilityAmount(visibilityAmount: Float) { + logger.logSetVisibilityAmount(visibilityAmount) mLinearVisibilityAmount = visibilityAmount mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount) handleAnimationFinished() @@ -517,6 +534,7 @@ constructor( private fun updateHideAmount() { val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount) val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount) + logger.logSetHideAmount(linearAmount) mStackScrollerController.setHideAmount(linearAmount, amount) notificationsFullyHidden = linearAmount == 1.0f } @@ -558,6 +576,7 @@ constructor( override fun dump(pw: PrintWriter, args: Array) { pw.println("inputLinearDozeAmount: $inputLinearDozeAmount") pw.println("inputEasedDozeAmount: $inputEasedDozeAmount") + pw.println("delayedDozeAmountOverride: $delayedDozeAmountOverride") pw.println("hardDozeAmountOverride: $hardDozeAmountOverride") pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource") pw.println("outputLinearDozeAmount: $outputLinearDozeAmount") @@ -593,4 +612,32 @@ constructor( */ @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {} } + + companion object { + private val notificationVisibility = + object : FloatProperty("notificationVisibility") { + + override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { + coordinator.setVisibilityAmount(value) + } + + override fun get(coordinator: NotificationWakeUpCoordinator): Float { + return coordinator.mLinearVisibilityAmount + } + } + + private val delayedDozeAmount = + object : FloatProperty("delayedDozeAmount") { + + override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { + coordinator.delayedDozeAmountOverride = value + coordinator.logger.logSetDelayDozeAmountOverride(value) + coordinator.updateDozeAmount() + } + + override fun get(coordinator: NotificationWakeUpCoordinator): Float { + return coordinator.delayedDozeAmountOverride + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt index d95d18ebba8cb..d3f935366f958 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt @@ -22,29 +22,40 @@ import javax.inject.Inject class NotificationWakeUpCoordinatorLogger @Inject constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { - private var lastSetDozeAmountLogWasFractional = false + private var allowThrottle = true + private var lastSetDozeAmountLogInputWasFractional = false + private var lastSetDozeAmountLogDelayWasFractional = false private var lastSetDozeAmountLogState = -1 private var lastSetHardOverride: Float? = null private var lastOnDozeAmountChangedLogWasFractional = false + private var lastSetDelayDozeAmountOverrideLogWasFractional = false + private var lastSetVisibilityAmountLogWasFractional = false + private var lastSetHideAmountLogWasFractional = false + private var lastSetHideAmount = -1f fun logUpdateDozeAmount( inputLinear: Float, + delayLinear: Float, hardOverride: Float?, outputLinear: Float, state: Int, changed: Boolean, ) { // Avoid logging on every frame of the animation if important values are not changing - val isFractional = inputLinear != 1f && inputLinear != 0f + val isInputFractional = inputLinear != 1f && inputLinear != 0f + val isDelayFractional = delayLinear != 1f && delayLinear != 0f if ( - lastSetDozeAmountLogWasFractional && - isFractional && + (isInputFractional || isDelayFractional) && + lastSetDozeAmountLogInputWasFractional == isInputFractional && + lastSetDozeAmountLogDelayWasFractional == isDelayFractional && lastSetDozeAmountLogState == state && - lastSetHardOverride == hardOverride + lastSetHardOverride == hardOverride && + allowThrottle ) { return } - lastSetDozeAmountLogWasFractional = isFractional + lastSetDozeAmountLogInputWasFractional = isInputFractional + lastSetDozeAmountLogDelayWasFractional = isDelayFractional lastSetDozeAmountLogState = state lastSetHardOverride = hardOverride @@ -55,11 +66,13 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { double1 = inputLinear.toDouble() str1 = hardOverride.toString() str2 = outputLinear.toString() + str3 = delayLinear.toString() int1 = state bool1 = changed }, { - "updateDozeAmount() inputLinear=$double1 hardOverride=$str1 outputLinear=$str2" + + "updateDozeAmount() inputLinear=$double1 delayLinear=$str3" + + " hardOverride=$str1 outputLinear=$str2" + " state=${StatusBarState.toString(int1)} changed=$bool1" } ) @@ -99,7 +112,7 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { fun logOnDozeAmountChanged(linear: Float, eased: Float) { // Avoid logging on every frame of the animation when values are fractional val isFractional = linear != 1f && linear != 0f - if (lastOnDozeAmountChangedLogWasFractional && isFractional) return + if (lastOnDozeAmountChangedLogWasFractional && isFractional && allowThrottle) return lastOnDozeAmountChangedLogWasFractional = isFractional buffer.log( TAG, @@ -112,6 +125,47 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { ) } + fun logSetDelayDozeAmountOverride(linear: Float) { + // Avoid logging on every frame of the animation when values are fractional + val isFractional = linear != 1f && linear != 0f + if (lastSetDelayDozeAmountOverrideLogWasFractional && isFractional && allowThrottle) return + lastSetDelayDozeAmountOverrideLogWasFractional = isFractional + buffer.log( + TAG, + DEBUG, + { double1 = linear.toDouble() }, + { "setDelayDozeAmountOverride($double1)" } + ) + } + + fun logSetVisibilityAmount(linear: Float) { + // Avoid logging on every frame of the animation when values are fractional + val isFractional = linear != 1f && linear != 0f + if (lastSetVisibilityAmountLogWasFractional && isFractional && allowThrottle) return + lastSetVisibilityAmountLogWasFractional = isFractional + buffer.log(TAG, DEBUG, { double1 = linear.toDouble() }, { "setVisibilityAmount($double1)" }) + } + + fun logSetHideAmount(linear: Float) { + // Avoid logging the same value repeatedly + if (lastSetHideAmount == linear && allowThrottle) return + lastSetHideAmount = linear + // Avoid logging on every frame of the animation when values are fractional + val isFractional = linear != 1f && linear != 0f + if (lastSetHideAmountLogWasFractional && isFractional && allowThrottle) return + lastSetHideAmountLogWasFractional = isFractional + buffer.log(TAG, DEBUG, { double1 = linear.toDouble() }, { "setHideAmount($double1)" }) + } + + fun logStartDelayedDozeAmountAnimation(alreadyRunning: Boolean) { + buffer.log( + TAG, + DEBUG, + { bool1 = alreadyRunning }, + { "startDelayedDozeAmountAnimation() alreadyRunning=$bool1" } + ) + } + fun logOnStateChanged(newState: Int, storedState: Int) { buffer.log( TAG, @@ -127,6 +181,18 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { ) } + fun logSetWakingUp(wakingUp: Boolean, requestDelayedAnimation: Boolean) { + buffer.log( + TAG, + DEBUG, + { + bool1 = wakingUp + bool2 = requestDelayedAnimation + }, + { "setWakingUp(wakingUp=$bool1, requestDelayedAnimation=$bool2)" } + ) + } + fun logClockTransitionAnimationStarting(delayWakeUpAnimation: Boolean) { buffer.log( TAG, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 2c088fa66a8a0..300587627c185 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5565,6 +5565,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public void setDozeAmount(float dozeAmount) { mAmbientState.setDozeAmount(dozeAmount); updateContinuousBackgroundDrawing(); + updateStackPosition(); requestChildrenUpdate(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt index 88f4ae98c54f5..bef9fcb5697cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt @@ -45,51 +45,130 @@ class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() { } @Test - fun updateDozeAmountWillThrottleFractionalInputUpdates() { - logger.logUpdateDozeAmount(0f, null, 0f, StatusBarState.SHADE, changed = false) + fun updateVisibilityThrottleFractionalUpdates() { + logger.logSetVisibilityAmount(0f) verifyDidLog(1) - logger.logUpdateDozeAmount(0.1f, null, 0.1f, StatusBarState.SHADE, changed = true) + logger.logSetVisibilityAmount(0.1f) verifyDidLog(1) - logger.logUpdateDozeAmount(0.2f, null, 0.2f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.3f, null, 0.3f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.4f, null, 0.4f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.5f, null, 0.5f, StatusBarState.SHADE, changed = true) + logger.logSetVisibilityAmount(0.2f) + logger.logSetVisibilityAmount(0.3f) + logger.logSetVisibilityAmount(0.4f) + logger.logSetVisibilityAmount(0.5f) verifyDidLog(0) - logger.logUpdateDozeAmount(1f, null, 1f, StatusBarState.SHADE, changed = true) + logger.logSetVisibilityAmount(1f) + verifyDidLog(1) + } + + @Test + fun updateHideAmountThrottleFractionalOrRepeatedUpdates() { + logger.logSetHideAmount(0f) + verifyDidLog(1) + logger.logSetHideAmount(0f) + logger.logSetHideAmount(0f) + verifyDidLog(0) + logger.logSetHideAmount(0.1f) + verifyDidLog(1) + logger.logSetHideAmount(0.2f) + logger.logSetHideAmount(0.3f) + logger.logSetHideAmount(0.4f) + logger.logSetHideAmount(0.5f) + logger.logSetHideAmount(0.5f) + logger.logSetHideAmount(0.5f) + verifyDidLog(0) + logger.logSetHideAmount(1f) + verifyDidLog(1) + logger.logSetHideAmount(1f) + logger.logSetHideAmount(1f) + verifyDidLog(0) + } + + @Test + fun updateDozeAmountWillThrottleFractionalInputUpdates() { + logger.logUpdateDozeAmount(0f, 0f, null, 0f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(0.1f, 0f, null, 0.1f, StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logUpdateDozeAmount(0.2f, 0f, null, 0.2f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.3f, 0f, null, 0.3f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.4f, 0f, null, 0.4f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logUpdateDozeAmount(1f, 0f, null, 1f, StatusBarState.SHADE, changed = true) + verifyDidLog(1) + } + + @Test + fun updateDozeAmountWillThrottleFractionalDelayUpdates() { + logger.logUpdateDozeAmount(0f, 0f, null, 0f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(0f, 0.1f, null, 0.1f, StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logUpdateDozeAmount(0f, 0.2f, null, 0.2f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0f, 0.3f, null, 0.3f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0f, 0.4f, null, 0.4f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0f, 0.5f, null, 0.5f, StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logUpdateDozeAmount(0f, 1f, null, 1f, StatusBarState.SHADE, changed = true) + verifyDidLog(1) + } + + @Test + fun updateDozeAmountWillIncludeFractionalUpdatesWhenOtherInputChangesFractionality() { + logger.logUpdateDozeAmount(0.0f, 1.0f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(0.1f, 1.0f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(0.2f, 1.0f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0.3f, 1.0f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0.4f, 1.0f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(0) + logger.logUpdateDozeAmount(0.5f, 0.9f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(0.6f, 0.8f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0.8f, 0.6f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0.9f, 0.5f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(0) + logger.logUpdateDozeAmount(1.0f, 0.4f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logUpdateDozeAmount(1.0f, 0.3f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(1.0f, 0.2f, 1f, 1f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(1.0f, 0.1f, 1f, 1f, StatusBarState.SHADE, changed = false) + verifyDidLog(0) + logger.logUpdateDozeAmount(1.0f, 0.0f, 1f, 1f, StatusBarState.SHADE, changed = false) verifyDidLog(1) } @Test fun updateDozeAmountWillIncludeFractionalUpdatesWhenStateChanges() { - logger.logUpdateDozeAmount(0f, null, 0f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0f, 0f, null, 0f, StatusBarState.SHADE, changed = false) verifyDidLog(1) - logger.logUpdateDozeAmount(0.1f, null, 0.1f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.1f, 0f, null, 0.1f, StatusBarState.SHADE, changed = true) verifyDidLog(1) - logger.logUpdateDozeAmount(0.2f, null, 0.2f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.3f, null, 0.3f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.4f, null, 0.4f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.5f, null, 0.5f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.2f, 0f, null, 0.2f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.3f, 0f, null, 0.3f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.4f, 0f, null, 0.4f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.SHADE, changed = true) verifyDidLog(0) - logger.logUpdateDozeAmount(0.5f, null, 0.5f, StatusBarState.KEYGUARD, changed = false) + logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.KEYGUARD, changed = false) verifyDidLog(1) } @Test fun updateDozeAmountWillIncludeFractionalUpdatesWhenHardOverrideChanges() { - logger.logUpdateDozeAmount(0f, null, 0f, StatusBarState.SHADE, changed = false) + logger.logUpdateDozeAmount(0f, 0f, null, 0f, StatusBarState.SHADE, changed = false) verifyDidLog(1) - logger.logUpdateDozeAmount(0.1f, null, 0.1f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.1f, 0f, null, 0.1f, StatusBarState.SHADE, changed = true) verifyDidLog(1) - logger.logUpdateDozeAmount(0.2f, null, 0.2f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.3f, null, 0.3f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.4f, null, 0.4f, StatusBarState.SHADE, changed = true) - logger.logUpdateDozeAmount(0.5f, null, 0.5f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.2f, 0f, null, 0.2f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.3f, 0f, null, 0.3f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.4f, 0f, null, 0.4f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.SHADE, changed = true) verifyDidLog(0) - logger.logUpdateDozeAmount(0.5f, 1f, 1f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, 1f, 1f, StatusBarState.SHADE, changed = true) verifyDidLog(1) - logger.logUpdateDozeAmount(0.5f, 0f, 0f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, 0f, 0f, StatusBarState.SHADE, changed = true) verifyDidLog(1) - logger.logUpdateDozeAmount(0.5f, null, 0.5f, StatusBarState.SHADE, changed = true) + logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.SHADE, changed = true) verifyDidLog(1) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index df98f87896673..6df62bb539d71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -17,12 +17,16 @@ package com.android.systemui.statusbar.notification import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.core.animation.AnimatorTestRule 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.shade.NotificationPanelViewController.WAKEUP_ANIMATION_DELAY_MS import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_WAKEUP import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -32,16 +36,21 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.anyFloat import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never import org.mockito.Mockito.verify @RunWith(AndroidTestingRunner::class) @SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) class NotificationWakeUpCoordinatorTest : SysuiTestCase() { + @get:Rule val animatorTestRule = AnimatorTestRule() + private val dumpManager: DumpManager = mock() private val headsUpManager: HeadsUpManager = mock() private val statusBarStateController: StatusBarStateController = mock() @@ -57,9 +66,8 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { private var bypassEnabled: Boolean = false private var statusBarState: Int = StatusBarState.KEYGUARD - private var dozeAmount: Float = 0f - private val easedDozeAmount: Float - get() = notificationWakeUpCoordinator.dozeAmountInterpolator.getInterpolation(dozeAmount) + private fun eased(dozeAmount: Float) = + notificationWakeUpCoordinator.dozeAmountInterpolator.getInterpolation(dozeAmount) private fun setBypassEnabled(enabled: Boolean) { bypassEnabled = enabled @@ -72,7 +80,6 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { } private fun setDozeAmount(dozeAmount: Float) { - this.dozeAmount = dozeAmount statusBarStateCallback.onDozeAmountChanged(dozeAmount, dozeAmount) } @@ -123,11 +130,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { @Test fun setDozeToHalfWillHalfShowNotifications() { setDozeAmount(0.5f) - verifyStackScrollerDozeAndHideAmount( - dozeAmount = easedDozeAmount, - hideAmount = 0.5f, - hideAmountEased = easedDozeAmount, - ) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() } @@ -135,7 +138,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { fun setDozeToZeroWithBypassWillFullyHideNotifications() { bypassEnabled = true setDozeAmount(0f) - verifyStackScrollerDozeAndHideAmount(dozeAmount = 01f, hideAmount = 1f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() } @@ -158,16 +161,91 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() { assertThat(notificationWakeUpCoordinator.statusBarState).isEqualTo(StatusBarState.SHADE) } - private fun verifyStackScrollerDozeAndHideAmount( - dozeAmount: Float, - hideAmount: Float, - hideAmountEased: Float? = null, - ) { + private val delayedDozeDelay = WAKEUP_ANIMATION_DELAY_MS.toLong() + private val delayedDozeDuration = ANIMATION_DURATION_WAKEUP.toLong() + + @Test + fun dozeAmountOutputClampsTo1WhenDelayStarts() { + notificationWakeUpCoordinator.setWakingUp(true, requestDelayedAnimation = true) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + + // verify further doze amount changes have no effect on output + setDozeAmount(0.5f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun verifyDozeAmountOutputTracksDelay() { + dozeAmountOutputClampsTo1WhenDelayStarts() + + // Animator waiting the delay amount should not yet affect the output + animatorTestRule.advanceTimeBy(delayedDozeDelay) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + + // input doze amount change to 0 has no effect + setDozeAmount(0.0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + + // Advancing the delay to 50% will cause the 50% output + animatorTestRule.advanceTimeBy(delayedDozeDuration / 2) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + + // Now advance delay to 100% completion; notifications become fully visible + animatorTestRule.advanceTimeBy(delayedDozeDuration / 2) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + + // Now advance delay to 200% completion -- should not invoke anything else + animatorTestRule.advanceTimeBy(delayedDozeDuration) + verify(stackScrollerController, never()).setDozeAmount(anyFloat()) + verify(stackScrollerController, never()).setHideAmount(anyFloat(), anyFloat()) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun verifyDelayedDozeAmountCanBeOverridden() { + dozeAmountOutputClampsTo1WhenDelayStarts() + + // input doze amount change to 0 has no effect + setDozeAmount(0.0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + + // Advancing the delay to 50% will cause the 50% output + animatorTestRule.advanceTimeBy(delayedDozeDelay + delayedDozeDuration / 2) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + + // Enabling bypass and showing keyguard will override back to fully dozing/hidden + setBypassEnabled(true) + setStatusBarState(StatusBarState.KEYGUARD) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun verifyRemovingOverrideRestoresOtherwiseCalculatedDozeAmount() { + verifyDelayedDozeAmountCanBeOverridden() + + // Disabling bypass will return back to the 50% value + setBypassEnabled(false) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + private fun verifyStackScrollerDozeAndHideAmount(dozeAmount: Float, hideAmount: Float) { // First verify that we did in-fact receive the correct values - verify(stackScrollerController).setDozeAmount(dozeAmount) - verify(stackScrollerController).setHideAmount(hideAmount, hideAmountEased ?: hideAmount) + verify(stackScrollerController).setDozeAmount(eased(dozeAmount)) + verify(stackScrollerController).setHideAmount(hideAmount, eased(hideAmount)) // Now verify that there was just this ONE call to each of these methods verify(stackScrollerController).setDozeAmount(anyFloat()) verify(stackScrollerController).setHideAmount(anyFloat(), anyFloat()) + // clear for next check + clearInvocations(stackScrollerController) } }