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:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -5565,6 +5565,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
public void setDozeAmount(float dozeAmount) {
|
||||
mAmbientState.setDozeAmount(dozeAmount);
|
||||
updateContinuousBackgroundDrawing();
|
||||
updateStackPosition();
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user