Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +16 −6 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import android.animation.ValueAnimator import android.content.Context import android.database.ContentObserver import android.os.Handler import android.os.PowerManager import android.provider.Settings import android.view.Surface import android.view.View Loading Loading @@ -54,9 +55,10 @@ class UnlockedScreenOffAnimationController @Inject constructor( private val keyguardStateController: KeyguardStateController, private val dozeParameters: dagger.Lazy<DozeParameters>, private val globalSettings: GlobalSettings, private val interactionJankMonitor: InteractionJankMonitor private val interactionJankMonitor: InteractionJankMonitor, private val powerManager: PowerManager, private val handler: Handler = Handler() ) : WakefulnessLifecycle.Observer, ScreenOffAnimation { private val handler = Handler() private lateinit var statusBar: StatusBar private lateinit var lightRevealScrim: LightRevealScrim Loading Loading @@ -231,10 +233,18 @@ class UnlockedScreenOffAnimationController @Inject constructor( lightRevealAnimationPlaying = true lightRevealAnimator.start() handler.postDelayed({ // Only run this callback if the device is sleeping (not interactive). This callback // is removed in onStartedWakingUp, but since that event is asynchronously // dispatched, a race condition could make it possible for this callback to be run // as the device is waking up. That results in the AOD UI being shown while we wake // up, with unpredictable consequences. if (!powerManager.isInteractive) { aodUiAnimationPlaying = true // Show AOD. That'll cause the KeyguardVisibilityHelper to call #animateInKeyguard. // Show AOD. That'll cause the KeyguardVisibilityHelper to call // #animateInKeyguard. statusBar.notificationPanelViewController.showAodUi() } }, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong()) return true Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +77 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.phone import android.animation.Animator import android.os.Handler import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View Loading @@ -29,13 +31,19 @@ import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.GlobalSettings import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.anyLong import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest Loading @@ -53,7 +61,9 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { @Mock private lateinit var globalSettings: GlobalSettings @Mock private lateinit var statusbar: StatusBar private lateinit var statusBar: StatusBar @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var lightRevealScrim: LightRevealScrim @Mock Loading @@ -62,6 +72,10 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { private lateinit var statusBarStateController: StatusBarStateControllerImpl @Mock private lateinit var interactionJankMonitor: InteractionJankMonitor @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var handler: Handler @Before fun setUp() { Loading @@ -75,9 +89,24 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { keyguardStateController, dagger.Lazy<DozeParameters> { dozeParameters }, globalSettings, interactionJankMonitor interactionJankMonitor, powerManager, handler = handler ) controller.initialize(statusbar, lightRevealScrim) controller.initialize(statusBar, lightRevealScrim) `when`(statusBar.notificationPanelViewController).thenReturn( notificationPanelViewController) // Screen off does not run if the panel is expanded, so we should say it's collapsed to test // screen off. `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true) } @After fun cleanUp() { // Tell the screen off controller to cancel the animations and clean up its state, or // subsequent tests will act unpredictably as the animator continues running. controller.onStartedWakingUp() } @Test Loading @@ -93,4 +122,49 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { listener.value.onAnimationEnd(null) Mockito.verify(animator).setListener(null) } /** * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal * animation to start. If the device is woken up during the screen off, we should *never* do * this. * * This test confirms that we do show the AOD UI when the device is not woken up * (PowerManager#isInteractive = false). */ @Test fun testAodUiShownIfNotInteractive() { `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(false) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) controller.startAnimation() verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) callbackCaptor.value.run() verify(notificationPanelViewController, times(1)).showAodUi() } /** * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal * animation to start. If the device is woken up during the screen off, we should *never* do * this. * * This test confirms that we do not show the AOD UI when the device is woken up during screen * off (PowerManager#isInteractive = true). */ @Test fun testAodUiNotShownIfInteractive() { `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(true) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) controller.startAnimation() verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) callbackCaptor.value.run() verify(notificationPanelViewController, never()).showAodUi() } } No newline at end of file Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +16 −6 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import android.animation.ValueAnimator import android.content.Context import android.database.ContentObserver import android.os.Handler import android.os.PowerManager import android.provider.Settings import android.view.Surface import android.view.View Loading Loading @@ -54,9 +55,10 @@ class UnlockedScreenOffAnimationController @Inject constructor( private val keyguardStateController: KeyguardStateController, private val dozeParameters: dagger.Lazy<DozeParameters>, private val globalSettings: GlobalSettings, private val interactionJankMonitor: InteractionJankMonitor private val interactionJankMonitor: InteractionJankMonitor, private val powerManager: PowerManager, private val handler: Handler = Handler() ) : WakefulnessLifecycle.Observer, ScreenOffAnimation { private val handler = Handler() private lateinit var statusBar: StatusBar private lateinit var lightRevealScrim: LightRevealScrim Loading Loading @@ -231,10 +233,18 @@ class UnlockedScreenOffAnimationController @Inject constructor( lightRevealAnimationPlaying = true lightRevealAnimator.start() handler.postDelayed({ // Only run this callback if the device is sleeping (not interactive). This callback // is removed in onStartedWakingUp, but since that event is asynchronously // dispatched, a race condition could make it possible for this callback to be run // as the device is waking up. That results in the AOD UI being shown while we wake // up, with unpredictable consequences. if (!powerManager.isInteractive) { aodUiAnimationPlaying = true // Show AOD. That'll cause the KeyguardVisibilityHelper to call #animateInKeyguard. // Show AOD. That'll cause the KeyguardVisibilityHelper to call // #animateInKeyguard. statusBar.notificationPanelViewController.showAodUi() } }, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong()) return true Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +77 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.phone import android.animation.Animator import android.os.Handler import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View Loading @@ -29,13 +31,19 @@ import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.GlobalSettings import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.anyLong import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest Loading @@ -53,7 +61,9 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { @Mock private lateinit var globalSettings: GlobalSettings @Mock private lateinit var statusbar: StatusBar private lateinit var statusBar: StatusBar @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var lightRevealScrim: LightRevealScrim @Mock Loading @@ -62,6 +72,10 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { private lateinit var statusBarStateController: StatusBarStateControllerImpl @Mock private lateinit var interactionJankMonitor: InteractionJankMonitor @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var handler: Handler @Before fun setUp() { Loading @@ -75,9 +89,24 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { keyguardStateController, dagger.Lazy<DozeParameters> { dozeParameters }, globalSettings, interactionJankMonitor interactionJankMonitor, powerManager, handler = handler ) controller.initialize(statusbar, lightRevealScrim) controller.initialize(statusBar, lightRevealScrim) `when`(statusBar.notificationPanelViewController).thenReturn( notificationPanelViewController) // Screen off does not run if the panel is expanded, so we should say it's collapsed to test // screen off. `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true) } @After fun cleanUp() { // Tell the screen off controller to cancel the animations and clean up its state, or // subsequent tests will act unpredictably as the animator continues running. controller.onStartedWakingUp() } @Test Loading @@ -93,4 +122,49 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { listener.value.onAnimationEnd(null) Mockito.verify(animator).setListener(null) } /** * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal * animation to start. If the device is woken up during the screen off, we should *never* do * this. * * This test confirms that we do show the AOD UI when the device is not woken up * (PowerManager#isInteractive = false). */ @Test fun testAodUiShownIfNotInteractive() { `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(false) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) controller.startAnimation() verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) callbackCaptor.value.run() verify(notificationPanelViewController, times(1)).showAodUi() } /** * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal * animation to start. If the device is woken up during the screen off, we should *never* do * this. * * This test confirms that we do not show the AOD UI when the device is woken up during screen * off (PowerManager#isInteractive = true). */ @Test fun testAodUiNotShownIfInteractive() { `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(true) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) controller.startAnimation() verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) callbackCaptor.value.run() verify(notificationPanelViewController, never()).showAodUi() } } No newline at end of file