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.shade
18 
19 import android.testing.AndroidTestingRunner
20 import android.view.View
21 import android.view.ViewGroup
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.R
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.plugins.statusbar.StatusBarStateController
26 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
27 import com.android.systemui.statusbar.StatusBarState.SHADE
28 import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
29 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
30 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
31 import com.android.systemui.util.mockito.capture
32 import com.android.systemui.util.mockito.whenever
33 import com.google.common.truth.Truth.assertThat
34 import org.junit.Before
35 import org.junit.Test
36 import org.junit.runner.RunWith
37 import org.mockito.ArgumentCaptor
38 import org.mockito.Captor
39 import org.mockito.Mock
40 import org.mockito.Mockito.atLeastOnce
41 import org.mockito.Mockito.verify
42 import org.mockito.MockitoAnnotations
43 
44 /**
45  * Translates items away/towards the hinge when the device is opened/closed. This is controlled by
46  * the set of ids, which also dictact which direction to move and when, via a filter fn.
47  */
48 @SmallTest
49 @RunWith(AndroidTestingRunner::class)
50 class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
51 
52     @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
53 
54     @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
55 
56     @Mock private lateinit var parent: ViewGroup
57 
58     @Mock private lateinit var splitShadeStatusBar: ViewGroup
59 
60     @Mock private lateinit var statusBarStateController: StatusBarStateController
61 
62     private lateinit var underTest: NotificationPanelUnfoldAnimationController
63     private lateinit var progressListeners: List<TransitionProgressListener>
64     private var xTranslationMax = 0f
65 
66     @Before
67     fun setup() {
68         MockitoAnnotations.initMocks(this)
69 
70         xTranslationMax =
71             context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat()
72 
73         underTest =
74             NotificationPanelUnfoldAnimationController(
75                 context,
76                 statusBarStateController,
77                 progressProvider
78             )
79         whenever(parent.findViewById<ViewGroup>(R.id.split_shade_status_bar)).thenReturn(
80             splitShadeStatusBar
81         )
82         underTest.setup(parent)
83 
84         verify(progressProvider, atLeastOnce()).addCallback(capture(progressListenerCaptor))
85         progressListeners = progressListenerCaptor.allValues
86     }
87 
88     @Test
89     fun whenInKeyguardState_viewDoesNotMove() {
90         whenever(statusBarStateController.getState()).thenReturn(KEYGUARD)
91 
92         val view = View(context)
93         whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
94 
95         onTransitionStarted()
96         assertThat(view.translationX).isZero()
97 
98         onTransitionProgress(0f)
99         assertThat(view.translationX).isZero()
100 
101         onTransitionProgress(0.5f)
102         assertThat(view.translationX).isZero()
103 
104         onTransitionFinished()
105         assertThat(view.translationX).isZero()
106     }
107 
108     @Test
109     fun whenInShadeState_viewDoesMove() {
110         whenever(statusBarStateController.getState()).thenReturn(SHADE)
111 
112         val view = View(context)
113         whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
114 
115         onTransitionStarted()
116         assertThat(view.translationX).isZero()
117 
118         onTransitionProgress(0f)
119         assertThat(view.translationX).isEqualTo(xTranslationMax)
120 
121         onTransitionProgress(0.5f)
122         assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
123 
124         onTransitionFinished()
125         assertThat(view.translationX).isZero()
126     }
127 
128     @Test
129     fun whenInShadeLockedState_viewDoesMove() {
130         whenever(statusBarStateController.getState()).thenReturn(SHADE_LOCKED)
131 
132         val view = View(context)
133         whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
134 
135         onTransitionStarted()
136         assertThat(view.translationX).isZero()
137 
138         onTransitionProgress(0f)
139         assertThat(view.translationX).isEqualTo(xTranslationMax)
140 
141         onTransitionProgress(0.5f)
142         assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
143 
144         onTransitionFinished()
145         assertThat(view.translationX).isZero()
146     }
147 
148     @Test
149     fun whenInKeyguardState_statusBarViewDoesNotMove() {
150         whenever(statusBarStateController.getState()).thenReturn(KEYGUARD)
151 
152         val view = View(context)
153         whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
154 
155         onTransitionStarted()
156         assertThat(view.translationX).isZero()
157 
158         onTransitionProgress(0f)
159         assertThat(view.translationX).isZero()
160 
161         onTransitionProgress(0.5f)
162         assertThat(view.translationX).isZero()
163 
164         onTransitionFinished()
165         assertThat(view.translationX).isZero()
166     }
167 
168     @Test
169     fun whenInShadeState_statusBarViewDoesMove() {
170         whenever(statusBarStateController.getState()).thenReturn(SHADE)
171 
172         val view = View(context)
173         whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
174 
175         onTransitionStarted()
176         assertThat(view.translationX).isZero()
177 
178         onTransitionProgress(0f)
179         assertThat(view.translationX).isEqualTo(xTranslationMax)
180 
181         onTransitionProgress(0.5f)
182         assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
183 
184         onTransitionFinished()
185         assertThat(view.translationX).isZero()
186     }
187 
188     @Test
189     fun whenInShadeLockedState_statusBarViewDoesMove() {
190         whenever(statusBarStateController.getState()).thenReturn(SHADE_LOCKED)
191 
192         val view = View(context)
193         whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
194         onTransitionStarted()
195         assertThat(view.translationX).isZero()
196 
197         onTransitionProgress(0f)
198         assertThat(view.translationX).isEqualTo(xTranslationMax)
199 
200         onTransitionProgress(0.5f)
201         assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
202 
203         onTransitionFinished()
204         assertThat(view.translationX).isZero()
205     }
206 
207     private fun onTransitionStarted() {
208         progressListeners.forEach { it.onTransitionStarted() }
209     }
210 
211     private fun onTransitionProgress(progress: Float) {
212         progressListeners.forEach { it.onTransitionProgress(progress) }
213     }
214 
215     private fun onTransitionFinished() {
216         progressListeners.forEach { it.onTransitionFinished() }
217     }
218 
219 }
220