1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.phone 18 19 import android.animation.Animator 20 import android.os.Handler 21 import android.os.PowerManager 22 import android.testing.AndroidTestingRunner 23 import android.testing.TestableLooper.RunWithLooper 24 import android.view.View 25 import androidx.test.filters.SmallTest 26 import com.android.internal.jank.InteractionJankMonitor 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.keyguard.KeyguardViewMediator 29 import com.android.systemui.keyguard.WakefulnessLifecycle 30 import com.android.systemui.shade.NotificationPanelViewController 31 import com.android.systemui.statusbar.LightRevealScrim 32 import com.android.systemui.statusbar.StatusBarStateControllerImpl 33 import com.android.systemui.statusbar.policy.KeyguardStateController 34 import com.android.systemui.util.settings.GlobalSettings 35 import junit.framework.Assert.assertFalse 36 import org.junit.After 37 import org.junit.Before 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 import org.mockito.ArgumentCaptor 41 import org.mockito.Mock 42 import org.mockito.Mockito 43 import org.mockito.Mockito.anyLong 44 import org.mockito.Mockito.never 45 import org.mockito.Mockito.spy 46 import org.mockito.Mockito.times 47 import org.mockito.Mockito.verify 48 import org.mockito.Mockito.`when` 49 import org.mockito.MockitoAnnotations 50 51 @SmallTest 52 @RunWith(AndroidTestingRunner::class) 53 @RunWithLooper(setAsMainLooper = true) 54 class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { 55 56 private lateinit var controller: UnlockedScreenOffAnimationController 57 @Mock 58 private lateinit var keyguardViewMediator: KeyguardViewMediator 59 @Mock 60 private lateinit var dozeParameters: DozeParameters 61 @Mock 62 private lateinit var keyguardStateController: KeyguardStateController 63 @Mock 64 private lateinit var globalSettings: GlobalSettings 65 @Mock 66 private lateinit var mCentralSurfaces: CentralSurfaces 67 @Mock 68 private lateinit var notificationPanelViewController: NotificationPanelViewController 69 @Mock 70 private lateinit var lightRevealScrim: LightRevealScrim 71 @Mock 72 private lateinit var wakefulnessLifecycle: WakefulnessLifecycle 73 @Mock 74 private lateinit var statusBarStateController: StatusBarStateControllerImpl 75 @Mock 76 private lateinit var interactionJankMonitor: InteractionJankMonitor 77 @Mock 78 private lateinit var powerManager: PowerManager 79 @Mock 80 private lateinit var handler: Handler 81 82 @Before setUpnull83 fun setUp() { 84 MockitoAnnotations.initMocks(this) 85 86 controller = UnlockedScreenOffAnimationController( 87 context, 88 wakefulnessLifecycle, 89 statusBarStateController, 90 dagger.Lazy<KeyguardViewMediator> { keyguardViewMediator }, 91 keyguardStateController, 92 dagger.Lazy<DozeParameters> { dozeParameters }, 93 globalSettings, 94 interactionJankMonitor, 95 powerManager, 96 handler = handler 97 ) 98 controller.initialize(mCentralSurfaces, lightRevealScrim) 99 `when`(mCentralSurfaces.notificationPanelViewController).thenReturn( 100 notificationPanelViewController) 101 102 // Screen off does not run if the panel is expanded, so we should say it's collapsed to test 103 // screen off. 104 `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true) 105 } 106 107 @After cleanUpnull108 fun cleanUp() { 109 // Tell the screen off controller to cancel the animations and clean up its state, or 110 // subsequent tests will act unpredictably as the animator continues running. 111 controller.onStartedWakingUp() 112 } 113 114 @Test testAnimClearsEndListenernull115 fun testAnimClearsEndListener() { 116 val keyguardView = View(context) 117 val animator = spy(keyguardView.animate()) 118 val keyguardSpy = spy(keyguardView) 119 Mockito.`when`(keyguardSpy.animate()).thenReturn(animator) 120 val listener = ArgumentCaptor.forClass(Animator.AnimatorListener::class.java) 121 val endAction = ArgumentCaptor.forClass(Runnable::class.java) 122 controller.animateInKeyguard(keyguardSpy, Runnable {}) 123 Mockito.verify(animator).setListener(listener.capture()) 124 Mockito.verify(animator).withEndAction(endAction.capture()) 125 126 // Verify that the listener is cleared if we cancel it. 127 listener.value.onAnimationCancel(null) 128 Mockito.verify(animator).setListener(null) 129 130 // Verify that the listener is also cleared if the end action is triggered. 131 endAction.value.run() 132 verify(animator, times(2)).setListener(null) 133 } 134 135 /** 136 * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal 137 * animation to start. If the device is woken up during the screen off, we should *never* do 138 * this. 139 * 140 * This test confirms that we do show the AOD UI when the device is not woken up 141 * (PowerManager#isInteractive = false). 142 */ 143 @Test testAodUiShownIfNotInteractivenull144 fun testAodUiShownIfNotInteractive() { 145 `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true) 146 `when`(powerManager.isInteractive).thenReturn(false) 147 148 val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) 149 controller.startAnimation() 150 151 verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) 152 153 callbackCaptor.value.run() 154 155 verify(notificationPanelViewController, times(1)).showAodUi() 156 } 157 158 /** 159 * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal 160 * animation to start. If the device is woken up during the screen off, we should *never* do 161 * this. 162 * 163 * This test confirms that we do not show the AOD UI when the device is woken up during screen 164 * off (PowerManager#isInteractive = true). 165 */ 166 @Test testAodUiNotShownIfInteractivenull167 fun testAodUiNotShownIfInteractive() { 168 `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true) 169 `when`(powerManager.isInteractive).thenReturn(true) 170 171 val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) 172 controller.startAnimation() 173 174 verify(handler).postDelayed(callbackCaptor.capture(), anyLong()) 175 callbackCaptor.value.run() 176 177 verify(notificationPanelViewController, never()).showAodUi() 178 } 179 180 @Test testNoAnimationPlaying_dozeParamsCanNotControlScreenOffnull181 fun testNoAnimationPlaying_dozeParamsCanNotControlScreenOff() { 182 `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(false) 183 184 assertFalse(controller.shouldPlayUnlockedScreenOffAnimation()) 185 controller.startAnimation() 186 assertFalse(controller.isAnimationPlaying()) 187 } 188 } 189