Merge changes from topic "tsuji-wakeup" into tm-qpr-dev

* changes:
  Create AnimatorTestRule2 which avoids the test interaction bugs with the original as it currently exists
  Keep the clock centered until the delayed doze animation plays.
  Delay the dozeAmount change animation.
  Run ktfmt on NotificationWakeUpCoordinator.kt
  Restructure the NotificationWakeUpCoordinator's dozeAmount recalculation.
  Tell NotificationWakeupCoordinator if we're waking from power button w/ SFPS press-to-unlock.
This commit is contained in:
Jeff DeCew
2023-03-29 20:03:21 +00:00
committed by Android (Google) Code Review
14 changed files with 1037 additions and 181 deletions

View File

@@ -254,6 +254,7 @@ public final class NotificationPanelViewController implements Dumpable {
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f;
public static final int WAKEUP_ANIMATION_DELAY_MS = 250;
private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG_DRAWABLE = false;
@@ -276,6 +277,7 @@ public final class NotificationPanelViewController implements Dumpable {
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
/**
* The factor of the usual high velocity that is needed in order to reach the maximum overshoot
* when flinging. A low value will make it that most flings will reach the maximum overshoot.
@@ -588,6 +590,12 @@ public final class NotificationPanelViewController implements Dumpable {
private boolean mGestureWaitForTouchSlop;
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
/**
* Whether we're waking up and will play the delayed doze animation in
* {@link NotificationWakeUpCoordinator}. If so, we'll want to keep the clock centered until the
* delayed doze animation starts.
*/
private boolean mWillPlayDelayedDozeAmountAnimation = false;
private final DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
@@ -1044,6 +1052,12 @@ public final class NotificationPanelViewController implements Dumpable {
requestScrollerTopPaddingUpdate(false /* animate */);
}
}
@Override
public void onDelayedDozeAmountAnimationRunning(boolean running) {
// On running OR finished, the animation is no longer waiting to play
setWillPlayDelayedDozeAmountAnimation(false);
}
});
mView.setRtlChangeListener(layoutDirection -> {
@@ -1640,11 +1654,28 @@ public final class NotificationPanelViewController implements Dumpable {
// Pulsing notification appears on the right. Move clock left to avoid overlap.
return false;
}
if (mWillPlayDelayedDozeAmountAnimation) {
return true;
}
// "Visible" notifications are actually not visible on AOD (unless pulsing), so it is safe
// to center the clock without overlap.
return isOnAod();
}
/**
* Notify us that {@link NotificationWakeUpCoordinator} is going to play the doze wakeup
* animation after a delay. If so, we'll keep the clock centered until that animation starts.
*/
public void setWillPlayDelayedDozeAmountAnimation(boolean willPlay) {
if (mWillPlayDelayedDozeAmountAnimation == willPlay) return;
mWillPlayDelayedDozeAmountAnimation = willPlay;
mWakeUpCoordinator.logDelayingClockWakeUpAnimation(willPlay);
// Once changing this value, see if we should move the clock.
positionClockAndNotifications();
}
private boolean isOnAod() {
return mDozing && mDozeParameters.getAlwaysOn();
}

View File

@@ -16,14 +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
@@ -36,12 +39,17 @@ 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.doOnEnd
import com.android.systemui.util.doOnStart
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
class NotificationWakeUpCoordinator
@Inject
constructor(
dumpManager: DumpManager,
private val mHeadsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
@@ -49,27 +57,25 @@ class NotificationWakeUpCoordinator @Inject constructor(
private val dozeParameters: DozeParameters,
private val screenOffAnimationController: ScreenOffAnimationController,
private val logger: NotificationWakeUpCoordinatorLogger,
) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, ShadeExpansionListener,
) :
OnHeadsUpChangedListener,
StatusBarStateController.StateListener,
ShadeExpansionListener,
Dumpable {
private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
"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 mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
private var mDozeAmountSource: String = "init"
private var mNotifsHiddenByDozeAmountOverride: Boolean = false
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"
private var outputLinearDozeAmount: Float = 0.0f
private var outputEasedDozeAmount: Float = 0.0f
@VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -84,27 +90,32 @@ class NotificationWakeUpCoordinator @Inject constructor(
var fullyAwake: Boolean = false
var wakingUp = false
set(value) {
private set(value) {
field = value
willWakeUp = false
if (value) {
if (mNotificationsVisible && !mNotificationsVisibleForExpansion &&
!bypassController.bypassEnabled) {
if (
mNotificationsVisible &&
!mNotificationsVisibleForExpansion &&
!bypassController.bypassEnabled
) {
// We're waking up while pulsing, let's make sure the animation looks nice
mStackScrollerController.wakeUpFromPulse()
}
if (bypassController.bypassEnabled && !mNotificationsVisible) {
// Let's make sure our huns become visible once we are waking up in case
// they were blocked by the proximity sensor
updateNotificationVisibility(animate = shouldAnimateVisibility(),
increaseSpeed = false)
updateNotificationVisibility(
animate = shouldAnimateVisibility(),
increaseSpeed = false
)
}
}
}
var willWakeUp = false
set(value) {
if (!value || mDozeAmount != 0.0f) {
if (!value || outputLinearDozeAmount != 0.0f) {
field = value
}
}
@@ -118,8 +129,10 @@ class NotificationWakeUpCoordinator @Inject constructor(
// Only when setting pulsing to true we want an immediate update, since we get
// this already when the doze service finishes which is usually before we get
// the waking up callback
updateNotificationVisibility(animate = shouldAnimateVisibility(),
increaseSpeed = false)
updateNotificationVisibility(
animate = shouldAnimateVisibility(),
increaseSpeed = false
)
}
}
@@ -133,17 +146,17 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
}
/**
* True if we can show pulsing heads up notifications
*/
/** True if we can show pulsing heads up notifications */
var canShowPulsingHuns: Boolean = false
private set
get() {
var canShow = pulsing
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
canShow = canShow || (wakingUp || willWakeUp || fullyAwake) &&
statusBarStateController.state == StatusBarState.KEYGUARD
canShow =
canShow ||
(wakingUp || willWakeUp || fullyAwake) &&
statusBarStateController.state == StatusBarState.KEYGUARD
// We want to hide the notifications when collapsed too much
if (collapsedEnoughToHide) {
canShow = false
@@ -152,30 +165,38 @@ class NotificationWakeUpCoordinator @Inject constructor(
return canShow
}
private val bypassStateChangedListener = object : OnBypassStateChangedListener {
override fun onBypassStateChanged(isEnabled: Boolean) {
// When the bypass state changes, we have to check whether we should re-show the
// notifications by clearing the doze amount override which hides them.
maybeClearDozeAmountOverrideHidingNotifs()
private val bypassStateChangedListener =
object : OnBypassStateChangedListener {
override fun onBypassStateChanged(isEnabled: Boolean) {
// When the bypass state changes, we have to check whether we should re-show the
// notifications by clearing the doze amount override which hides them.
maybeClearHardDozeAmountOverrideHidingNotifs()
}
}
}
init {
dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
addListener(object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
if (isFullyHidden && mNotificationsVisibleForExpansion) {
// When the notification becomes fully invisible, let's make sure our expansion
// flag also changes. This can happen if the bouncer shows when dragging down
// and then the screen turning off, where we don't reset this state.
setNotificationsVisibleForExpansion(visible = false, animate = false,
increaseSpeed = false)
addListener(
object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
if (isFullyHidden && mNotificationsVisibleForExpansion) {
// When the notification becomes fully invisible, let's make sure our
// expansion
// flag also changes. This can happen if the bouncer shows when dragging
// down
// and then the screen turning off, where we don't reset this state.
setNotificationsVisibleForExpansion(
visible = false,
animate = false,
increaseSpeed = false
)
}
}
}
})
)
}
fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
@@ -221,15 +242,17 @@ class NotificationWakeUpCoordinator @Inject constructor(
wakeUpListeners.remove(listener)
}
private fun updateNotificationVisibility(
animate: Boolean,
increaseSpeed: Boolean
) {
private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
visible = visible && canShowPulsingHuns
if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) {
if (
!visible &&
mNotificationsVisible &&
(wakingUp || willWakeUp) &&
outputLinearDozeAmount != 0.0f
) {
// let's not make notifications invisible while waking up, otherwise the animation
// is strange
return
@@ -257,7 +280,9 @@ class NotificationWakeUpCoordinator @Inject constructor(
override fun onDozeAmountChanged(linear: Float, eased: Float) {
logger.logOnDozeAmountChanged(linear = linear, eased = eased)
if (overrideDozeAmountIfAnimatingScreenOff(linear)) {
inputLinearDozeAmount = linear
inputEasedDozeAmount = eased
if (overrideDozeAmountIfAnimatingScreenOff()) {
return
}
@@ -265,33 +290,109 @@ class NotificationWakeUpCoordinator @Inject constructor(
return
}
if (linear != 1.0f && linear != 0.0f &&
(mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
if (clearHardDozeAmountOverride()) {
return
}
setDozeAmount(linear, eased, source = "StatusBar")
updateDozeAmount()
}
fun setDozeAmount(
linear: Float,
eased: Float,
source: String,
hidesNotifsByOverride: Boolean = false
) {
val changed = linear != mLinearDozeAmount
logger.logSetDozeAmount(linear, eased, source, statusBarStateController.state, changed)
mLinearDozeAmount = linear
mDozeAmount = eased
mDozeAmountSource = source
mNotifsHiddenByDozeAmountOverride = hidesNotifsByOverride
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
setNotificationsVisibleForExpansion(visible = false, animate = false,
increaseSpeed = false)
private fun setHardDozeAmountOverride(dozing: Boolean, source: String) {
logger.logSetDozeAmountOverride(dozing = dozing, source = source)
hardDozeAmountOverride = if (dozing) 1f else 0f
hardDozeAmountOverrideSource = source
updateDozeAmount()
}
private fun clearHardDozeAmountOverride(): Boolean {
if (hardDozeAmountOverride == null) return false
hardDozeAmountOverride = null
hardDozeAmountOverrideSource = "Cleared: $hardDozeAmountOverrideSource"
updateDozeAmount()
return true
}
private fun updateDozeAmount() {
// Calculate new doze amount (linear)
val newOutputLinearDozeAmount =
hardDozeAmountOverride ?: max(inputLinearDozeAmount, delayedDozeAmountOverride)
val changed = outputLinearDozeAmount != newOutputLinearDozeAmount
// notify when the animation is starting
if (
newOutputLinearDozeAmount != 1.0f &&
newOutputLinearDozeAmount != 0.0f &&
(outputLinearDozeAmount == 0.0f || outputLinearDozeAmount == 1.0f)
) {
// Let's notify the scroller that an animation started
notifyAnimationStart(outputLinearDozeAmount == 1.0f)
}
// Update output doze amount
outputLinearDozeAmount = newOutputLinearDozeAmount
outputEasedDozeAmount = dozeAmountInterpolator.getInterpolation(outputLinearDozeAmount)
logger.logUpdateDozeAmount(
inputLinear = inputLinearDozeAmount,
delayLinear = delayedDozeAmountOverride,
hardOverride = hardDozeAmountOverride,
outputLinear = outputLinearDozeAmount,
state = statusBarStateController.state,
changed = changed
)
mStackScrollerController.setDozeAmount(outputEasedDozeAmount)
updateHideAmount()
if (changed && outputLinearDozeAmount == 0.0f) {
setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
setNotificationsVisibleForExpansion(
visible = false,
animate = false,
increaseSpeed = false
)
}
}
/**
* Notifies the wakeup coordinator that we're waking up.
*
* [requestDelayedAnimation] is used to request that we delay the start of the wakeup animation
* in order to wait for a potential fingerprint authentication to arrive, since unlocking during
* the wakeup animation looks chaotic.
*
* If called with [wakingUp] and [requestDelayedAnimation] both `true`, the [WakeUpListener]s
* are guaranteed to receive at least one [WakeUpListener.onDelayedDozeAmountAnimationRunning]
* call with `false` at some point in the near future. A call with `true` before that will
* happen if the animation is not already running.
*/
fun setWakingUp(
wakingUp: Boolean,
requestDelayedAnimation: Boolean,
) {
logger.logSetWakingUp(wakingUp, requestDelayedAnimation)
this.wakingUp = wakingUp
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()
doOnStart {
wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(true) }
}
doOnEnd {
delayedDozeAmountAnimator = null
wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(false) }
}
start()
}
}
override fun onStateChanged(newState: Int) {
@@ -302,12 +403,15 @@ class NotificationWakeUpCoordinator @Inject constructor(
// undefined state, so it's an indication that we should do state cleanup. We override
// the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
// See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
setHardDozeAmountOverride(
dozing = false,
source = "Override: Shade->Shade (lock cancelled by unlock)"
)
this.state = newState
return
}
if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
if (overrideDozeAmountIfAnimatingScreenOff()) {
this.state = newState
return
}
@@ -317,7 +421,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
return
}
maybeClearDozeAmountOverrideHidingNotifs()
maybeClearHardDozeAmountOverrideHidingNotifs()
this.state = newState
}
@@ -340,15 +444,14 @@ class NotificationWakeUpCoordinator @Inject constructor(
/**
* @return Whether the doze amount was overridden because bypass is enabled. If true, the
* original doze amount should be ignored.
* original doze amount should be ignored.
*/
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)",
hidesNotifsByOverride = true)
setHardDozeAmountOverride(dozing = true, source = "Override: bypass (keyguard)")
} else {
setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
setHardDozeAmountOverride(dozing = false, source = "Override: bypass (shade)")
}
return true
}
@@ -362,26 +465,28 @@ class NotificationWakeUpCoordinator @Inject constructor(
* This fixes bugs where the bypass state changing could result in stale overrides, hiding
* notifications either on the inside screen or even after unlock.
*/
private fun maybeClearDozeAmountOverrideHidingNotifs() {
if (mNotifsHiddenByDozeAmountOverride) {
private fun maybeClearHardDozeAmountOverrideHidingNotifs() {
if (hardDozeAmountOverride == 1f) {
val onKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
val dozing = statusBarStateController.isDozing
val bypass = bypassController.bypassEnabled
val animating =
screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()
screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()
// Overrides are set by [overrideDozeAmountIfAnimatingScreenOff] and
// [overrideDozeAmountIfBypass] based on 'animating' and 'bypass' respectively, so only
// clear the override if both those conditions are cleared. But also require either
// !dozing or !onKeyguard because those conditions should indicate that we intend
// notifications to be visible, and thus it is safe to unhide them.
val willRemove = (!onKeyguard || !dozing) && !bypass && !animating
logger.logMaybeClearDozeAmountOverrideHidingNotifs(
willRemove = willRemove,
onKeyguard = onKeyguard, dozing = dozing,
bypass = bypass, animating = animating,
logger.logMaybeClearHardDozeAmountOverrideHidingNotifs(
willRemove = willRemove,
onKeyguard = onKeyguard,
dozing = dozing,
bypass = bypass,
animating = animating,
)
if (willRemove) {
setDozeAmount(0f, 0f, source = "Removed: $mDozeAmountSource")
clearHardDozeAmountOverride()
}
}
}
@@ -392,12 +497,11 @@ class NotificationWakeUpCoordinator @Inject constructor(
* off and dozeAmount goes from 1f to 0f.
*
* @return Whether the doze amount was overridden because we are playing the screen off
* animation. If true, the original doze amount should be ignored.
* animation. If true, the original doze amount should be ignored.
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
private fun overrideDozeAmountIfAnimatingScreenOff(): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
setDozeAmount(1f, 1f, source = "Override: animating screen off",
hidesNotifsByOverride = true)
setHardDozeAmountOverride(dozing = true, source = "Override: animating screen off")
return true
}
@@ -406,41 +510,41 @@ class NotificationWakeUpCoordinator @Inject constructor(
private fun startVisibilityAnimation(increaseSpeed: Boolean) {
if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
mVisibilityInterpolator = if (mNotificationsVisible)
Interpolators.TOUCH_RESPONSE
else
Interpolators.FAST_OUT_SLOW_IN_REVERSE
mVisibilityInterpolator =
if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE
else Interpolators.FAST_OUT_SLOW_IN_REVERSE
}
val target = if (mNotificationsVisible) 1.0f else 0.0f
val visibilityAnimator = ObjectAnimator.ofFloat(this, mNotificationVisibility, target)
visibilityAnimator.setInterpolator(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()
}
visibilityAnimator.setDuration(duration)
visibilityAnimator.duration = duration
visibilityAnimator.start()
mVisibilityAnimator = visibilityAnimator
}
private fun setVisibilityAmount(visibilityAmount: Float) {
logger.logSetVisibilityAmount(visibilityAmount)
mLinearVisibilityAmount = visibilityAmount
mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
visibilityAmount)
mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount)
handleAnimationFinished()
updateHideAmount()
}
private fun handleAnimationFinished() {
if (mLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
mEntrySetToClearWhenFinished.clear()
}
}
private fun updateHideAmount() {
val linearAmount = min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
val amount = min(1.0f - mVisibilityAmount, mDozeAmount)
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
}
@@ -458,7 +562,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
var animate = shouldAnimateVisibility()
if (!isHeadsUp) {
if (mLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
if (entry.isRowDismissed) {
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
@@ -477,13 +581,16 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
private fun shouldAnimateVisibility() =
dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("mLinearDozeAmount: $mLinearDozeAmount")
pw.println("mDozeAmount: $mDozeAmount")
pw.println("mDozeAmountSource: $mDozeAmountSource")
pw.println("mNotifsHiddenByDozeAmountOverride: $mNotifsHiddenByDozeAmountOverride")
pw.println("inputLinearDozeAmount: $inputLinearDozeAmount")
pw.println("inputEasedDozeAmount: $inputEasedDozeAmount")
pw.println("delayedDozeAmountOverride: $delayedDozeAmountOverride")
pw.println("hardDozeAmountOverride: $hardDozeAmountOverride")
pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource")
pw.println("outputLinearDozeAmount: $outputLinearDozeAmount")
pw.println("outputEasedDozeAmount: $outputEasedDozeAmount")
pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
pw.println("mNotificationsVisible: $mNotificationsVisible")
pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
@@ -500,16 +607,53 @@ class NotificationWakeUpCoordinator @Inject constructor(
pw.println("canShowPulsingHuns: $canShowPulsingHuns")
}
fun logDelayingClockWakeUpAnimation(delayingAnimation: Boolean) {
logger.logDelayingClockWakeUpAnimation(delayingAnimation)
}
interface WakeUpListener {
/**
* Called whenever the notifications are fully hidden or shown
*/
/** Called whenever the notifications are fully hidden or shown */
@JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
/**
* Called whenever the pulseExpansion changes
*
* @param expandingChanged if the user has started or stopped expanding
*/
@JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
@JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
}
companion object {
private val notificationVisibility =
object : FloatProperty<NotificationWakeUpCoordinator>("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<NotificationWakeUpCoordinator>("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
}
}
}
}

View File

@@ -22,50 +22,75 @@ 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 lastSetDozeAmountLogSource = "undefined"
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 logSetDozeAmount(
linear: Float,
eased: Float,
source: String,
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 = linear != 1f && linear != 0f
val isInputFractional = inputLinear != 1f && inputLinear != 0f
val isDelayFractional = delayLinear != 1f && delayLinear != 0f
if (
lastSetDozeAmountLogWasFractional &&
isFractional &&
(isInputFractional || isDelayFractional) &&
lastSetDozeAmountLogInputWasFractional == isInputFractional &&
lastSetDozeAmountLogDelayWasFractional == isDelayFractional &&
lastSetDozeAmountLogState == state &&
lastSetDozeAmountLogSource == source
lastSetHardOverride == hardOverride &&
allowThrottle
) {
return
}
lastSetDozeAmountLogWasFractional = isFractional
lastSetDozeAmountLogInputWasFractional = isInputFractional
lastSetDozeAmountLogDelayWasFractional = isDelayFractional
lastSetDozeAmountLogState = state
lastSetDozeAmountLogSource = source
lastSetHardOverride = hardOverride
buffer.log(
TAG,
DEBUG,
{
double1 = linear.toDouble()
str2 = eased.toString()
str3 = source
double1 = inputLinear.toDouble()
str1 = hardOverride.toString()
str2 = outputLinear.toString()
str3 = delayLinear.toString()
int1 = state
bool1 = changed
},
{
"setDozeAmount(linear=$double1, eased=$str2, source=$str3)" +
"updateDozeAmount() inputLinear=$double1 delayLinear=$str3" +
" hardOverride=$str1 outputLinear=$str2" +
" state=${StatusBarState.toString(int1)} changed=$bool1"
}
)
}
fun logMaybeClearDozeAmountOverrideHidingNotifs(
fun logSetDozeAmountOverride(dozing: Boolean, source: String) {
buffer.log(
TAG,
DEBUG,
{
bool1 = dozing
str1 = source
},
{ "setDozeAmountOverride(dozing=$bool1, source=\"$str1\")" }
)
}
fun logMaybeClearHardDozeAmountOverrideHidingNotifs(
willRemove: Boolean,
onKeyguard: Boolean,
dozing: Boolean,
@@ -80,14 +105,14 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) {
"willRemove=$willRemove onKeyguard=$onKeyguard dozing=$dozing" +
" bypass=$bypass animating=$animating"
},
{ "maybeClearDozeAmountOverrideHidingNotifs() $str1" }
{ "maybeClearHardDozeAmountOverrideHidingNotifs() $str1" }
)
}
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,
@@ -100,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,
@@ -114,6 +180,27 @@ 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 logDelayingClockWakeUpAnimation(delayingAnimation: Boolean) {
buffer.log(
TAG,
DEBUG,
{ bool1 = delayingAnimation },
{ "logDelayingClockWakeUpAnimation($bool1)" }
)
}
}
private const val TAG = "NotificationWakeUpCoordinator"

View File

@@ -5565,6 +5565,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void setDozeAmount(float dozeAmount) {
mAmbientState.setDozeAmount(dozeAmount);
updateContinuousBackgroundDrawing();
updateStackPosition();
requestChildrenUpdate();
}

View File

@@ -53,6 +53,7 @@ import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.util.Compile;
import java.io.PrintWriter;
@@ -71,6 +72,7 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
boolean DEBUG_CAMERA_LIFT = false;
boolean DEBUG_WINDOW_STATE = false;
boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
// additional instrumentation for testing purposes; intended to be left on during development
boolean CHATTY = DEBUG;
boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
@@ -536,6 +538,8 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void extendDozePulse();
boolean shouldDelayWakeUpAnimation();
public static class KeyboardShortcutsMessage {
final int mDeviceId;

View File

@@ -69,6 +69,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
@@ -257,6 +258,7 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import dagger.Lazy;
@@ -446,10 +448,20 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
private LightRevealScrim mLightRevealScrim;
private PowerButtonReveal mPowerButtonReveal;
private boolean mWakeUpComingFromTouch;
/**
* Whether we should delay the wakeup animation (which shows the notifications and moves the
* clock view). This is typically done when waking up from a 'press to unlock' gesture on a
* device with a side fingerprint sensor, so that if the fingerprint scan is successful, we
* can play the unlock animation directly rather than interrupting the wakeup animation part
* way through.
*/
private boolean mShouldDelayWakeUpAnimation = false;
private final Object mQueueLock = new Object();
private final PulseExpansionHandler mPulseExpansionHandler;
@@ -510,6 +522,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
private final UserTracker mUserTracker;
private final Provider<FingerprintManager> mFingerprintManager;
private CentralSurfacesComponent mCentralSurfacesComponent;
@@ -753,7 +766,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
Lazy<CameraLauncher> cameraLauncherLazy,
Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy,
AlternateBouncerInteractor alternateBouncerInteractor,
UserTracker userTracker
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -834,6 +848,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mCameraLauncherLazy = cameraLauncherLazy;
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUserTracker = userTracker;
mFingerprintManager = fingerprintManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -3137,6 +3152,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
public boolean shouldDelayWakeUpAnimation() {
return mShouldDelayWakeUpAnimation;
}
private void updateDozingState() {
Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
Trace.beginSection("CentralSurfaces#updateDozingState");
@@ -3147,11 +3166,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
boolean keyguardVisibleOrWillBe =
keyguardVisible || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
boolean wakeAndUnlock = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
|| (mDozing && mDozeParameters.shouldControlScreenOff()
&& keyguardVisibleOrWillBe);
boolean animate = (!mDozing && shouldAnimateDozeWakeup())
|| (mDozing && mDozeParameters.shouldControlScreenOff() && keyguardVisibleOrWillBe);
mNotificationPanelViewController.setDozing(mDozing, animate);
updateQsExpansionEnabled();
@@ -3504,7 +3520,44 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
DejankUtils.startDetectingBlockingIpcs(tag);
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mDeviceInteractive = true;
mWakeUpCoordinator.setWakingUp(true);
if (shouldAnimateDozeWakeup()) {
// If this is false, the power button must be physically pressed in order to
// trigger fingerprint authentication.
final boolean touchToUnlockAnytime = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
-1,
mUserTracker.getUserId()) > 0;
// Delay if we're waking up, not mid-doze animation (which means we are
// cancelling a sleep), from the power button, on a device with a power button
// FPS, and 'press to unlock' is required.
mShouldDelayWakeUpAnimation =
!isPulsing()
&& mStatusBarStateController.getDozeAmount() == 1f
&& mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON
&& mFingerprintManager.get().isPowerbuttonFps()
&& mFingerprintManager.get().hasEnrolledFingerprints()
&& !touchToUnlockAnytime;
if (DEBUG_WAKEUP_DELAY) {
Log.d(TAG, "mShouldDelayWakeUpAnimation=" + mShouldDelayWakeUpAnimation);
}
} else {
// If we're not animating anyway, we do not need to delay it.
mShouldDelayWakeUpAnimation = false;
if (DEBUG_WAKEUP_DELAY) {
Log.d(TAG, "mShouldDelayWakeUpAnimation CLEARED");
}
}
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(
mShouldDelayWakeUpAnimation);
mWakeUpCoordinator.setWakingUp(
/* wakingUp= */ true,
mShouldDelayWakeUpAnimation);
if (!mKeyguardBypassController.getBypassEnabled()) {
mHeadsUpManager.releaseAllImmediately();
}
@@ -3531,7 +3584,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onFinishedWakingUp() {
mWakeUpCoordinator.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false);
mWakeUpCoordinator.setWakingUp(false, false);
if (mKeyguardStateController.isOccluded()
&& !mDozeParameters.canControlUnlockedScreenOff()) {
// When the keyguard is occluded we don't use the KEYGUARD state which would
@@ -4395,4 +4448,15 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
return mUserTracker.getUserHandle();
}
/**
* Whether we want to animate the wake animation AOD to lockscreen. This is done only if the
* doze service host says we can, and also we're not wake and unlocking (in which case the
* AOD instantly hides).
*/
private boolean shouldAnimateDozeWakeup() {
return mDozeServiceHost.shouldAnimateWakeup()
&& mBiometricUnlockController.getMode()
!= BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2023 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 androidx.core.animation;
import android.os.Looper;
import android.os.SystemClock;
import android.util.AndroidRuntimeException;
import androidx.annotation.NonNull;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* NOTE: this is a copy of the {@link androidx.core.animation.AnimatorTestRule} which attempts to
* circumvent the problems with {@link androidx.core.animation.AnimationHandler} having a static
* list of callbacks.
*
* TODO(b/275602127): remove this and use the original rule once we have the updated androidx code.
*/
public final class AnimatorTestRule2 implements TestRule {
class TestAnimationHandler extends AnimationHandler {
TestAnimationHandler() {
super(new TestProvider());
}
List<AnimationFrameCallback> animationCallbacks = new ArrayList<>();
@Override
void addAnimationFrameCallback(AnimationFrameCallback callback) {
animationCallbacks.add(callback);
callback.doAnimationFrame(getCurrentTime());
}
@Override
public void removeCallback(AnimationFrameCallback callback) {
int id = animationCallbacks.indexOf(callback);
if (id >= 0) {
animationCallbacks.set(id, null);
}
}
void onAnimationFrame(long frameTime) {
for (int i = 0; i < animationCallbacks.size(); i++) {
final AnimationFrameCallback callback = animationCallbacks.get(i);
if (callback == null) {
continue;
}
callback.doAnimationFrame(frameTime);
}
}
@Override
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
for (int i = animationCallbacks.size() - 1; i >= 0; i--) {
AnimationFrameCallback cb = animationCallbacks.get(i);
if (cb == null) {
continue;
}
if (objectAnimator.shouldAutoCancel(cb)) {
((Animator) animationCallbacks.get(i)).cancel();
}
}
}
}
final TestAnimationHandler mTestHandler;
final long mStartTime;
private long mTotalTimeDelta = 0;
private final Object mLock = new Object();
public AnimatorTestRule2() {
mStartTime = SystemClock.uptimeMillis();
mTestHandler = new TestAnimationHandler();
}
@NonNull
@Override
public Statement apply(@NonNull final Statement base, @NonNull Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
AnimationHandler.setTestHandler(mTestHandler);
try {
base.evaluate();
} finally {
AnimationHandler.setTestHandler(null);
}
}
};
}
/**
* Advances the animation clock by the given amount of delta in milliseconds. This call will
* produce an animation frame to all the ongoing animations. This method needs to be
* called on the same thread as {@link Animator#start()}.
*
* @param timeDelta the amount of milliseconds to advance
*/
public void advanceTimeBy(long timeDelta) {
if (Looper.myLooper() == null) {
// Throw an exception
throw new AndroidRuntimeException("AnimationTestRule#advanceTimeBy(long) may only be"
+ "called on Looper threads");
}
synchronized (mLock) {
// Advance time & pulse a frame
mTotalTimeDelta += timeDelta < 0 ? 0 : timeDelta;
}
// produce a frame
mTestHandler.onAnimationFrame(getCurrentTime());
}
/**
* Returns the current time in milliseconds tracked by AnimationHandler. Note that this is a
* different time than the time tracked by {@link SystemClock} This method needs to be called on
* the same thread as {@link Animator#start()}.
*/
public long getCurrentTime() {
if (Looper.myLooper() == null) {
// Throw an exception
throw new AndroidRuntimeException("AnimationTestRule#getCurrentTime() may only be"
+ "called on Looper threads");
}
synchronized (mLock) {
return mStartTime + mTotalTimeDelta;
}
}
private class TestProvider implements AnimationHandler.AnimationFrameCallbackProvider {
TestProvider() {
}
@Override
public void onNewCallbackAdded(AnimationHandler.AnimationFrameCallback callback) {
callback.doAnimationFrame(getCurrentTime());
}
@Override
public void postFrameCallback() {
}
@Override
public void setFrameDelay(long delay) {
}
@Override
public long getFrameDelay() {
return 0;
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2023 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 androidx.core.animation
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.doOnEnd
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
class AnimatorTestRuleTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule2()
@Test
fun testA() {
didTouchA = false
didTouchB = false
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchA = true }
start()
}
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 150
doOnEnd { didTouchA = true }
start()
}
animatorTestRule.advanceTimeBy(100)
assertThat(didTouchA).isTrue()
assertThat(didTouchB).isFalse()
}
@Test
fun testB() {
didTouchA = false
didTouchB = false
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchB = true }
start()
}
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 150
doOnEnd { didTouchB = true }
start()
}
animatorTestRule.advanceTimeBy(100)
assertThat(didTouchA).isFalse()
assertThat(didTouchB).isTrue()
}
companion object {
var didTouchA = false
var didTouchB = false
}
}

View File

@@ -436,6 +436,34 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertKeyguardStatusViewCentered();
}
@Test
public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
assertKeyguardStatusViewCentered();
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false);
assertKeyguardStatusViewNotCentered();
}
@Test
public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
assertKeyguardStatusViewCentered();
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(false);
assertKeyguardStatusViewCentered();
}
@Test
public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
mStatusBarStateController.setState(KEYGUARD);

View File

@@ -22,7 +22,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
import androidx.core.animation.AnimatorTestRule
import androidx.core.animation.AnimatorTestRule2
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -70,7 +70,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler
private val fakeFeatureFlags = FakeFeatureFlags()
@get:Rule val animatorTestRule = AnimatorTestRule()
@get:Rule val animatorTestRule = AnimatorTestRule2()
@Before
fun setup() {

View File

@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogcatEchoTracker
@@ -45,47 +45,130 @@ class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() {
}
@Test
fun setDozeAmountWillThrottleFractionalUpdates() {
logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false)
fun updateVisibilityThrottleFractionalUpdates() {
logger.logSetVisibilityAmount(0f)
verifyDidLog(1)
logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetVisibilityAmount(0.1f)
verifyDidLog(1)
logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetVisibilityAmount(0.2f)
logger.logSetVisibilityAmount(0.3f)
logger.logSetVisibilityAmount(0.4f)
logger.logSetVisibilityAmount(0.5f)
verifyDidLog(0)
logger.logSetDozeAmount(1f, 1f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetVisibilityAmount(1f)
verifyDidLog(1)
}
@Test
fun setDozeAmountWillIncludeFractionalUpdatesWhenStateChanges() {
logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false)
fun updateHideAmountThrottleFractionalOrRepeatedUpdates() {
logger.logSetHideAmount(0f)
verifyDidLog(1)
logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true)
verifyDidLog(1)
logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetHideAmount(0f)
logger.logSetHideAmount(0f)
verifyDidLog(0)
logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.KEYGUARD, changed = false)
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 setDozeAmountWillIncludeFractionalUpdatesWhenSourceChanges() {
logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false)
fun updateDozeAmountWillThrottleFractionalDelayUpdates() {
logger.logUpdateDozeAmount(0f, 0f, null, 0f, StatusBarState.SHADE, changed = false)
verifyDidLog(1)
logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true)
logger.logUpdateDozeAmount(0f, 0.1f, null, 0.1f, StatusBarState.SHADE, changed = true)
verifyDidLog(1)
logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true)
logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true)
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.logSetDozeAmount(0.5f, 0.5f, "source2", StatusBarState.SHADE, changed = false)
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, 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(0.5f, 0f, null, 0.5f, StatusBarState.KEYGUARD, changed = false)
verifyDidLog(1)
}
@Test
fun updateDozeAmountWillIncludeFractionalUpdatesWhenHardOverrideChanges() {
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(0.5f, 0f, 1f, 1f, StatusBarState.SHADE, changed = true)
verifyDidLog(1)
logger.logUpdateDozeAmount(0.5f, 0f, 0f, 0f, StatusBarState.SHADE, changed = true)
verifyDidLog(1)
logger.logUpdateDozeAmount(0.5f, 0f, null, 0.5f, StatusBarState.SHADE, changed = true)
verifyDidLog(1)
}

View File

@@ -17,31 +17,42 @@
package com.android.systemui.statusbar.notification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.core.animation.AnimatorTestRule2
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase;
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
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
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
import org.mockito.Mockito.verifyNoMoreInteractions
@RunWith(AndroidTestingRunner::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule2()
private val dumpManager: DumpManager = mock()
private val headsUpManager: HeadsUpManager = mock()
private val statusBarStateController: StatusBarStateController = mock()
@@ -50,6 +61,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
private val screenOffAnimationController: ScreenOffAnimationController = mock()
private val logger: NotificationWakeUpCoordinatorLogger = mock()
private val stackScrollerController: NotificationStackScrollLayoutController = mock()
private val wakeUpListener: NotificationWakeUpCoordinator.WakeUpListener = mock()
private lateinit var notificationWakeUpCoordinator: NotificationWakeUpCoordinator
private lateinit var statusBarStateCallback: StatusBarStateController.StateListener
@@ -57,7 +69,8 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
private var bypassEnabled: Boolean = false
private var statusBarState: Int = StatusBarState.KEYGUARD
private var dozeAmount: Float = 0f
private fun eased(dozeAmount: Float) =
notificationWakeUpCoordinator.dozeAmountInterpolator.getInterpolation(dozeAmount)
private fun setBypassEnabled(enabled: Boolean) {
bypassEnabled = enabled
@@ -70,7 +83,6 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
}
private fun setDozeAmount(dozeAmount: Float) {
this.dozeAmount = dozeAmount
statusBarStateCallback.onDozeAmountChanged(dozeAmount, dozeAmount)
}
@@ -129,7 +141,7 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
fun setDozeToZeroWithBypassWillFullyHideNotifications() {
bypassEnabled = true
setDozeAmount(0f)
verifyStackScrollerDozeAndHideAmount(dozeAmount = 01f, hideAmount = 1f)
verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f)
assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue()
}
@@ -152,12 +164,161 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
assertThat(notificationWakeUpCoordinator.statusBarState).isEqualTo(StatusBarState.SHADE)
}
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 verifyWakeUpListenerCallbacksWhenDozing() {
// prime internal state as dozing, then add the listener
setDozeAmount(1f)
notificationWakeUpCoordinator.addListener(wakeUpListener)
setDozeAmount(0.5f)
verify(wakeUpListener).onFullyHiddenChanged(eq(false))
verifyNoMoreInteractions(wakeUpListener)
clearInvocations(wakeUpListener)
setDozeAmount(0f)
verifyNoMoreInteractions(wakeUpListener)
setDozeAmount(0.5f)
verifyNoMoreInteractions(wakeUpListener)
setDozeAmount(1f)
verify(wakeUpListener).onFullyHiddenChanged(eq(true))
verifyNoMoreInteractions(wakeUpListener)
}
@Test
fun verifyWakeUpListenerCallbacksWhenDelayingAnimation() {
// prime internal state as dozing, then add the listener
setDozeAmount(1f)
notificationWakeUpCoordinator.addListener(wakeUpListener)
// setWakingUp() doesn't do anything yet
notificationWakeUpCoordinator.setWakingUp(true, requestDelayedAnimation = true)
verifyNoMoreInteractions(wakeUpListener)
// verify further doze amount changes have no effect
setDozeAmount(0.5f)
verifyNoMoreInteractions(wakeUpListener)
// advancing to just before the start time should not invoke the listener
animatorTestRule.advanceTimeBy(delayedDozeDelay - 1)
verifyNoMoreInteractions(wakeUpListener)
animatorTestRule.advanceTimeBy(1)
verify(wakeUpListener).onDelayedDozeAmountAnimationRunning(eq(true))
verifyNoMoreInteractions(wakeUpListener)
clearInvocations(wakeUpListener)
// input doze amount change to 0 has no effect
setDozeAmount(0.0f)
verifyNoMoreInteractions(wakeUpListener)
// Advancing the delay to 50% will cause notifications to no longer be fully hidden
animatorTestRule.advanceTimeBy(delayedDozeDuration / 2)
verify(wakeUpListener).onFullyHiddenChanged(eq(false))
verifyNoMoreInteractions(wakeUpListener)
clearInvocations(wakeUpListener)
// Now advance delay to 99.x% completion; notifications become fully visible
animatorTestRule.advanceTimeBy(delayedDozeDuration / 2 - 1)
verifyNoMoreInteractions(wakeUpListener)
// advance to 100%; animation no longer running
animatorTestRule.advanceTimeBy(1)
verify(wakeUpListener).onDelayedDozeAmountAnimationRunning(eq(false))
verifyNoMoreInteractions(wakeUpListener)
clearInvocations(wakeUpListener)
// Now advance delay to 200% completion -- should not invoke anything else
animatorTestRule.advanceTimeBy(delayedDozeDuration)
verifyNoMoreInteractions(wakeUpListener)
}
@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, 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)
}
}

View File

@@ -311,6 +311,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private ViewRootImpl mViewRootImpl;
@Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Mock private UserTracker mUserTracker;
@Mock private FingerprintManager mFingerprintManager;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@Mock IPowerManager mPowerManagerService;
@@ -521,7 +522,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCameraLauncherLazy,
() -> mLightRevealScrimViewModel,
mAlternateBouncerInteractor,
mUserTracker
mUserTracker,
() -> mFingerprintManager
) {
@Override
protected ViewRootImpl getViewRootImpl() {

View File

@@ -62,7 +62,7 @@ import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.core.animation.AnimatorTestRule;
import androidx.core.animation.AnimatorTestRule2;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -110,7 +110,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
private final UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@ClassRule
public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
public static AnimatorTestRule2 mAnimatorTestRule = new AnimatorTestRule2();
@Before
public void setUp() throws Exception {