Merge changes from topic "tsuji-wakeup" into tm-qpr-dev am: e29ff09035
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22325397 Change-Id: If044489ed5903d85242748b4aeb6b018fb45cacb Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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();
|
||||
@@ -3505,7 +3521,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();
|
||||
}
|
||||
@@ -3532,7 +3585,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
|
||||
@@ -4396,4 +4449,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