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