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.unfold
18 
19 import android.hardware.devicestate.DeviceStateManager
20 import android.hardware.devicestate.DeviceStateManager.FoldStateListener
21 import android.provider.Settings
22 import android.testing.AndroidTestingRunner
23 import androidx.test.filters.SmallTest
24 import com.android.internal.util.LatencyTracker
25 import com.android.systemui.SysuiTestCase
26 import com.android.systemui.keyguard.ScreenLifecycle
27 import com.android.systemui.unfold.util.FoldableDeviceStates
28 import com.android.systemui.unfold.util.FoldableTestUtils
29 import com.android.systemui.util.mockito.any
30 import org.junit.Before
31 import org.junit.Test
32 import org.junit.runner.RunWith
33 import org.mockito.ArgumentCaptor
34 import org.mockito.Captor
35 import org.mockito.Mock
36 import org.mockito.Mockito.never
37 import org.mockito.Mockito.verify
38 import org.mockito.Mockito.verifyNoMoreInteractions
39 import org.mockito.MockitoAnnotations
40 import java.util.Optional
41 
42 @RunWith(AndroidTestingRunner::class)
43 @SmallTest
44 class UnfoldLatencyTrackerTest : SysuiTestCase() {
45 
46     @Mock
47     lateinit var latencyTracker: LatencyTracker
48 
49     @Mock
50     lateinit var deviceStateManager: DeviceStateManager
51 
52     @Mock
53     lateinit var screenLifecycle: ScreenLifecycle
54 
55     @Captor
56     private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
57 
58     @Captor
59     private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
60 
61     private lateinit var deviceStates: FoldableDeviceStates
62 
63     private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker
64 
65     private val transitionProgressProvider = TestUnfoldTransitionProvider()
66 
67     @Before
68     fun setUp() {
69         MockitoAnnotations.initMocks(this)
70         unfoldLatencyTracker = UnfoldLatencyTracker(
71             latencyTracker,
72             deviceStateManager,
73             Optional.of(transitionProgressProvider),
74             context.mainExecutor,
75             context,
76             context.contentResolver,
77             screenLifecycle
78         ).apply { init() }
79         deviceStates = FoldableTestUtils.findDeviceStates(context)
80 
81         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
82         verify(screenLifecycle).addObserver(screenLifecycleCaptor.capture())
83     }
84 
85     @Test
86     fun unfold_startedFolded_animationsDisabled_eventPropagatedOnScreenTurnedOnEvent() {
87         setAnimationsEnabled(false)
88         sendFoldEvent(folded = true)
89         sendFoldEvent(folded = false)
90 
91         sendScreenTurnedOnEvent()
92 
93         verify(latencyTracker).onActionStart(any())
94         verify(latencyTracker).onActionEnd(any())
95     }
96 
97     @Test
98     fun unfold_startedFolded_animationsEnabledOnScreenTurnedOn_eventNotFinished() {
99         setAnimationsEnabled(true)
100         sendFoldEvent(folded = true)
101         sendFoldEvent(folded = false)
102 
103         sendScreenTurnedOnEvent()
104 
105         verify(latencyTracker).onActionStart(any())
106         verify(latencyTracker, never()).onActionEnd(any())
107     }
108 
109     @Test
110     fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
111         setAnimationsEnabled(true)
112         sendFoldEvent(folded = false)
113 
114         sendScreenTurnedOnEvent()
115         transitionProgressProvider.onTransitionStarted()
116 
117         verifyNoMoreInteractions(latencyTracker)
118     }
119 
120     @Test
121     fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
122         setAnimationsEnabled(true)
123         sendFoldEvent(folded = true)
124         sendFoldEvent(folded = false)
125 
126         sendScreenTurnedOnEvent()
127         transitionProgressProvider.onTransitionStarted()
128 
129         verify(latencyTracker).onActionStart(any())
130         verify(latencyTracker).onActionEnd(any())
131     }
132 
133     @Test
134     fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
135         setAnimationsEnabled(true)
136         sendFoldEvent(folded = false)
137         sendFoldEvent(folded = true)
138         sendFoldEvent(folded = false)
139 
140         sendScreenTurnedOnEvent()
141         transitionProgressProvider.onTransitionStarted()
142 
143         verify(latencyTracker).onActionStart(any())
144         verify(latencyTracker).onActionEnd(any())
145     }
146 
147     @Test
148     fun fold_animationsDisabled_screenTurnedOn_eventNotPropagated() {
149         setAnimationsEnabled(false)
150         sendFoldEvent(folded = true)
151 
152         sendScreenTurnedOnEvent() // outer display on.
153 
154         verifyNoMoreInteractions(latencyTracker)
155     }
156 
157     @Test
158     fun fold_animationsEnabled_screenTurnedOn_eventNotPropagated() {
159         setAnimationsEnabled(true)
160         sendFoldEvent(folded = true)
161 
162         sendScreenTurnedOnEvent() // outer display on.
163         transitionProgressProvider.onTransitionStarted()
164 
165         verifyNoMoreInteractions(latencyTracker)
166     }
167 
168     @Test
169     fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() {
170         sendScreenTurnedOnEvent()
171 
172         verifyNoMoreInteractions(latencyTracker)
173     }
174 
175     private fun sendFoldEvent(folded: Boolean) {
176         val state = if (folded) deviceStates.folded else deviceStates.unfolded
177         foldStateListenerCaptor.value.onStateChanged(state)
178     }
179 
180     private fun sendScreenTurnedOnEvent() {
181         screenLifecycleCaptor.value.onScreenTurnedOn()
182     }
183 
184     private fun setAnimationsEnabled(enabled: Boolean) {
185         val durationScale =
186             if (enabled) {
187                 1f
188             } else {
189                 0f
190             }
191 
192         // It uses [TestableSettingsProvider] and it will be cleared after the test
193         Settings.Global.putString(
194             context.contentResolver,
195             Settings.Global.ANIMATOR_DURATION_SCALE,
196             durationScale.toString()
197         )
198     }
199 }