Keep the clock centered until the delayed doze animation plays.
This prevents the clock from moving until the notifications come down. Bug: 269085199 Test: atest NotificationPanelViewTest Change-Id: Iaf3f5f6ba3dd5b7a62b4e41d857069f7c8cb888e
This commit is contained in:
@@ -589,6 +589,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;
|
||||
@@ -1045,6 +1051,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 -> {
|
||||
@@ -1641,11 +1653,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();
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassSta
|
||||
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager
|
||||
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
|
||||
import com.android.systemui.util.doOnCancel
|
||||
import com.android.systemui.util.doOnEnd
|
||||
import com.android.systemui.util.doOnStart
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.max
|
||||
@@ -357,6 +357,11 @@ constructor(
|
||||
* [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,
|
||||
@@ -379,8 +384,13 @@ constructor(
|
||||
interpolator = InterpolatorsAndroidX.LINEAR
|
||||
duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
|
||||
startDelay = WAKEUP_ANIMATION_DELAY_MS.toLong()
|
||||
doOnEnd { delayedDozeAmountAnimator = null }
|
||||
doOnCancel { delayedDozeAmountAnimator = null }
|
||||
doOnStart {
|
||||
wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(true) }
|
||||
}
|
||||
doOnEnd {
|
||||
delayedDozeAmountAnimator = null
|
||||
wakeUpListeners.forEach { it.onDelayedDozeAmountAnimationRunning(false) }
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
@@ -597,8 +607,8 @@ constructor(
|
||||
pw.println("canShowPulsingHuns: $canShowPulsingHuns")
|
||||
}
|
||||
|
||||
fun logClockTransitionAnimationStarting(delayWakeUpAnimation: Boolean) {
|
||||
logger.logClockTransitionAnimationStarting(delayWakeUpAnimation)
|
||||
fun logDelayingClockWakeUpAnimation(delayingAnimation: Boolean) {
|
||||
logger.logDelayingClockWakeUpAnimation(delayingAnimation)
|
||||
}
|
||||
|
||||
interface WakeUpListener {
|
||||
@@ -611,6 +621,12 @@ constructor(
|
||||
* @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 {
|
||||
|
||||
@@ -193,12 +193,12 @@ constructor(@NotificationLockscreenLog private val buffer: LogBuffer) {
|
||||
)
|
||||
}
|
||||
|
||||
fun logClockTransitionAnimationStarting(delayWakeUpAnimation: Boolean) {
|
||||
fun logDelayingClockWakeUpAnimation(delayingAnimation: Boolean) {
|
||||
buffer.log(
|
||||
TAG,
|
||||
DEBUG,
|
||||
{ bool1 = delayWakeUpAnimation },
|
||||
{ "clockTransitionAnimationStarting() withDelay=$bool1" }
|
||||
{ bool1 = delayingAnimation },
|
||||
{ "logDelayingClockWakeUpAnimation($bool1)" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3564,6 +3564,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|
||||
}
|
||||
}
|
||||
|
||||
mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(
|
||||
mShouldDelayWakeUpAnimation);
|
||||
mWakeUpCoordinator.setWakingUp(
|
||||
/* wakingUp= */ true,
|
||||
mShouldDelayWakeUpAnimation);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -31,6 +31,7 @@ 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
|
||||
@@ -43,6 +44,7 @@ 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
|
||||
@@ -59,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
|
||||
@@ -207,6 +210,76 @@ class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user