1 /* 2 * Copyright (C) 2020 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 18 19 import android.animation.ObjectAnimator 20 import android.testing.AndroidTestingRunner 21 import android.testing.TestableLooper 22 import androidx.test.filters.SmallTest 23 import com.android.internal.jank.InteractionJankMonitor 24 import com.android.internal.logging.testing.UiEventLoggerFake 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.dump.DumpManager 27 import com.android.systemui.plugins.statusbar.StatusBarStateController 28 import com.android.systemui.shade.ShadeExpansionStateManager 29 import org.junit.Assert.assertEquals 30 import org.junit.Assert.assertFalse 31 import org.junit.Assert.assertTrue 32 import org.junit.Before 33 import org.junit.Test 34 import org.junit.runner.RunWith 35 import org.mockito.ArgumentMatchers.any 36 import org.mockito.ArgumentMatchers.anyFloat 37 import org.mockito.ArgumentMatchers.anyInt 38 import org.mockito.ArgumentMatchers.eq 39 import org.mockito.Mock 40 import org.mockito.Mockito 41 import org.mockito.Mockito.mock 42 import org.mockito.Mockito.verify 43 import org.mockito.Mockito.`when` as whenever 44 import org.mockito.MockitoAnnotations 45 46 @SmallTest 47 @RunWith(AndroidTestingRunner::class) 48 @TestableLooper.RunWithLooper 49 class StatusBarStateControllerImplTest : SysuiTestCase() { 50 51 @Mock lateinit var interactionJankMonitor: InteractionJankMonitor 52 @Mock private lateinit var mockDarkAnimator: ObjectAnimator 53 @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager 54 55 private lateinit var controller: StatusBarStateControllerImpl 56 private lateinit var uiEventLogger: UiEventLoggerFake 57 58 @Before 59 fun setUp() { 60 MockitoAnnotations.initMocks(this) 61 whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true) 62 whenever(interactionJankMonitor.end(anyInt())).thenReturn(true) 63 64 uiEventLogger = UiEventLoggerFake() 65 controller = object : StatusBarStateControllerImpl( 66 uiEventLogger, 67 mock(DumpManager::class.java), 68 interactionJankMonitor, shadeExpansionStateManager 69 ) { 70 override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator } 71 } 72 } 73 74 @Test 75 fun testChangeState_logged() { 76 TestableLooper.get(this).runWithLooper { 77 controller.state = StatusBarState.KEYGUARD 78 controller.state = StatusBarState.SHADE 79 controller.state = StatusBarState.SHADE_LOCKED 80 } 81 82 val logs = uiEventLogger.logs 83 assertEquals(3, logs.size) 84 val ids = logs.map(UiEventLoggerFake.FakeUiEvent::eventId) 85 assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[0]) 86 assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[1]) 87 assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[2]) 88 } 89 90 @Test 91 fun testSetDozeAmountInternal_onlySetsOnce() { 92 val listener = mock(StatusBarStateController.StateListener::class.java) 93 controller.addCallback(listener) 94 95 controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */) 96 controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */) 97 verify(listener).onDozeAmountChanged(eq(0.5f), anyFloat()) 98 } 99 100 @Test 101 fun testSetState_appliesState_sameStateButDifferentUpcomingState() { 102 controller.state = StatusBarState.SHADE 103 controller.setUpcomingState(StatusBarState.KEYGUARD) 104 105 assertEquals(controller.state, StatusBarState.SHADE) 106 107 // We should return true (state change was applied) despite going from SHADE to SHADE, since 108 // the upcoming state was set to KEYGUARD. 109 assertTrue(controller.setState(StatusBarState.SHADE)) 110 } 111 112 @Test 113 fun testSetState_appliesState_differentStateEqualToUpcomingState() { 114 controller.state = StatusBarState.SHADE 115 controller.setUpcomingState(StatusBarState.KEYGUARD) 116 117 assertEquals(controller.state, StatusBarState.SHADE) 118 119 // Make sure we apply a SHADE -> KEYGUARD state change when the upcoming state is KEYGUARD. 120 assertTrue(controller.setState(StatusBarState.KEYGUARD)) 121 } 122 123 @Test 124 fun testSetState_doesNotApplyState_currentAndUpcomingStatesSame() { 125 controller.state = StatusBarState.SHADE 126 controller.setUpcomingState(StatusBarState.SHADE) 127 128 assertEquals(controller.state, StatusBarState.SHADE) 129 130 // We're going from SHADE -> SHADE, and the upcoming state is also SHADE, this should not do 131 // anything. 132 assertFalse(controller.setState(StatusBarState.SHADE)) 133 134 // Double check that we can still force it to happen. 135 assertTrue(controller.setState(StatusBarState.SHADE, true /* force */)) 136 } 137 138 @Test 139 fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() { 140 // Put controller in AOD state 141 controller.setAndInstrumentDozeAmount(null, 1f, false) 142 143 // When waking from doze, CentralSurfaces#updateDozingState will update the dozing state 144 // before the doze amount changes 145 controller.setIsDozing(false) 146 147 // Animate the doze amount to 0f, as would normally happen 148 controller.setAndInstrumentDozeAmount(null, 0f, true) 149 150 // Check that the doze amount is immediately set to a value slightly less than 1f. This is 151 // to ensure that any scrim implementation changes its opacity immediately rather than 152 // waiting an extra frame. Waiting an extra frame will cause a relayout (which is expensive) 153 // and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ. 154 assertEquals(0.99f, controller.dozeAmount, 0.009f) 155 } 156 157 @Test 158 fun testSetDreamState_invokesCallback() { 159 val listener = mock(StatusBarStateController.StateListener::class.java) 160 controller.addCallback(listener) 161 162 controller.setIsDreaming(true) 163 verify(listener).onDreamingChanged(true) 164 165 Mockito.clearInvocations(listener) 166 167 controller.setIsDreaming(false) 168 verify(listener).onDreamingChanged(false) 169 } 170 171 @Test 172 fun testSetDreamState_getterReturnsCurrentState() { 173 controller.setIsDreaming(true) 174 assertTrue(controller.isDreaming()) 175 176 controller.setIsDreaming(false) 177 assertFalse(controller.isDreaming()) 178 } 179 } 180