blob: 691d8716dee39d8cd6b42488a11c10979fd4c4b4 [file] [log] [blame]
Lucas Dupin9e3fa102017-11-08 17:16:55 -08001/*
2 * Copyright (C) 2017 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
17package com.android.systemui.statusbar.phone;
18
19import static org.mockito.Mockito.mock;
20import static org.mockito.Mockito.never;
21import static org.mockito.Mockito.times;
22import static org.mockito.Mockito.verify;
23import static org.mockito.Mockito.when;
24
25import android.animation.Animator;
26import android.graphics.Color;
27import android.os.Handler;
28import android.os.Looper;
29import android.support.test.filters.SmallTest;
30import android.testing.AndroidTestingRunner;
31import android.testing.TestableLooper;
32import android.view.View;
33
Lucas Dupin7517b5d2017-08-22 12:51:25 -070034import com.android.keyguard.KeyguardUpdateMonitor;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080035import com.android.systemui.SysuiTestCase;
36import com.android.systemui.statusbar.ScrimView;
37import com.android.systemui.util.wakelock.WakeLock;
38import com.android.systemui.utils.os.FakeHandler;
39
40import org.junit.Assert;
41import org.junit.Before;
42import org.junit.Test;
43import org.junit.runner.RunWith;
44
45import java.util.function.Consumer;
46
47@RunWith(AndroidTestingRunner.class)
48@TestableLooper.RunWithLooper
49@SmallTest
50public class ScrimControllerTest extends SysuiTestCase {
51
52 private SynchronousScrimController mScrimController;
53 private ScrimView mScrimBehind;
54 private ScrimView mScrimInFront;
55 private View mHeadsUpScrim;
56 private Consumer<Boolean> mScrimVisibilityCallback;
57 private Boolean mScrimVisibile;
58 private LightBarController mLightBarController;
59 private DozeParameters mDozeParamenters;
60 private WakeLock mWakeLock;
61 private boolean mAlwaysOnEnabled;
62
63 @Before
64 public void setup() {
65 mLightBarController = mock(LightBarController.class);
66 mScrimBehind = new ScrimView(getContext());
67 mScrimInFront = new ScrimView(getContext());
68 mHeadsUpScrim = mock(View.class);
69 mWakeLock = mock(WakeLock.class);
70 mAlwaysOnEnabled = true;
71 mScrimVisibilityCallback = (Boolean visible) -> mScrimVisibile = visible;
72 mDozeParamenters = mock(DozeParameters.class);
73 when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
Lucas Dupin43d0d732017-11-16 11:23:49 -080074 when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080075 mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
76 mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters);
77 }
78
79 @Test
80 public void initialState() {
81 Assert.assertEquals("ScrimController should start initialized",
82 mScrimController.getState(), ScrimState.UNINITIALIZED);
83 }
84
85 @Test
86 public void transitionToKeyguard() {
87 mScrimController.transitionTo(ScrimState.KEYGUARD);
88 mScrimController.finishAnimationsImmediately();
89 // Front scrim should be transparent
90 // Back scrim should be visible without tint
91 assertScrimVisibility(false /* front */, true /* behind */);
92 assertScrimTint(mScrimBehind, false /* tinted */);
93 }
94
95 @Test
Lucas Dupin7517b5d2017-08-22 12:51:25 -070096 public void transitionToAod_withRegularWallpaper() {
97 mScrimController.transitionTo(ScrimState.AOD);
98 mScrimController.finishAnimationsImmediately();
99 // Front scrim should be transparent
100 // Back scrim should be visible with tint
101 assertScrimVisibility(false /* front */, true /* behind */);
102 assertScrimTint(mScrimBehind, true /* tinted */);
103 assertScrimTint(mScrimInFront, true /* tinted */);
104 }
105
106 @Test
107 public void transitionToAod_withAodWallpaper() {
108 mScrimController.setWallpaperSupportsAmbientMode(true);
109 mScrimController.transitionTo(ScrimState.AOD);
110 mScrimController.finishAnimationsImmediately();
111 // Front scrim should be transparent
112 // Back scrim should be transparent
113 assertScrimVisibility(false /* front */, false /* behind */);
114 }
115
116 @Test
117 public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
118 ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
119 @Override
120 public boolean hasLockscreenWallpaper() {
121 return true;
122 }
123 };
124 mScrimController.setWallpaperSupportsAmbientMode(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800125 mScrimController.transitionTo(ScrimState.AOD);
126 mScrimController.finishAnimationsImmediately();
127 // Front scrim should be transparent
128 // Back scrim should be visible with tint
129 assertScrimVisibility(false /* front */, true /* behind */);
130 assertScrimTint(mScrimBehind, true /* tinted */);
131 assertScrimTint(mScrimInFront, true /* tinted */);
132 }
133
134 @Test
135 public void transitionToPulsing() {
136 mScrimController.transitionTo(ScrimState.PULSING);
137 mScrimController.finishAnimationsImmediately();
138 // Front scrim should be transparent
139 // Back scrim should be visible with tint
140 // Pulse callback should have been invoked
141 assertScrimVisibility(false /* front */, true /* behind */);
142 assertScrimTint(mScrimBehind, true /* tinted */);
143 }
144
145 @Test
146 public void transitionToBouncer() {
147 mScrimController.transitionTo(ScrimState.BOUNCER);
148 mScrimController.finishAnimationsImmediately();
149 // Front scrim should be transparent
150 // Back scrim should be visible without tint
151 assertScrimVisibility(true /* front */, true /* behind */);
152 assertScrimTint(mScrimBehind, false /* tinted */);
153 }
154
155 @Test
156 public void transitionToUnlocked() {
157 mScrimController.transitionTo(ScrimState.UNLOCKED);
158 mScrimController.finishAnimationsImmediately();
159 // Front scrim should be transparent
160 // Back scrim should be transparent
161 assertScrimVisibility(false /* front */, false /* behind */);
162 assertScrimTint(mScrimBehind, false /* tinted */);
163 assertScrimTint(mScrimInFront, false /* tinted */);
164
165 // Back scrim should be visible after start dragging
166 mScrimController.setPanelExpansion(0.5f);
167 assertScrimVisibility(false /* front */, true /* behind */);
168 }
169
170 @Test
171 public void transitionToUnlockedFromAod() {
172 // Simulate unlock with fingerprint
173 mScrimController.transitionTo(ScrimState.AOD);
174 mScrimController.finishAnimationsImmediately();
175 mScrimController.transitionTo(ScrimState.UNLOCKED);
176 // Immediately tinted after the transition starts
177 assertScrimTint(mScrimInFront, true /* tinted */);
178 assertScrimTint(mScrimBehind, true /* tinted */);
179 mScrimController.finishAnimationsImmediately();
180 // Front scrim should be transparent
181 // Back scrim should be transparent
182 // Neither scrims should be tinted anymore after the animation.
183 assertScrimVisibility(false /* front */, false /* behind */);
184 assertScrimTint(mScrimInFront, false /* tinted */);
185 assertScrimTint(mScrimBehind, false /* tinted */);
186 }
187
188 @Test
189 public void scrimBlanksBeforeLeavingAoD() {
190 // Simulate unlock with fingerprint
191 mScrimController.transitionTo(ScrimState.AOD);
192 mScrimController.finishAnimationsImmediately();
193 mScrimController.transitionTo(ScrimState.UNLOCKED,
194 new ScrimController.Callback() {
195 @Override
196 public void onDisplayBlanked() {
197 // Front scrim should be black in the middle of the transition
198 Assert.assertTrue("Scrim should be visible during transition. Alpha: "
199 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
200 assertScrimTint(mScrimInFront, true /* tinted */);
201 Assert.assertTrue("Scrim should be visible during transition.",
202 mScrimVisibile);
203 }
204 });
205 mScrimController.finishAnimationsImmediately();
206 }
207
208 @Test
209 public void testScrimCallback() {
210 int[] callOrder = {0, 0, 0};
211 int[] currentCall = {0};
212 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
213 @Override
214 public void onStart() {
215 callOrder[0] = ++currentCall[0];
216 }
217
218 @Override
219 public void onDisplayBlanked() {
220 callOrder[1] = ++currentCall[0];
221 }
222
223 @Override
224 public void onFinished() {
225 callOrder[2] = ++currentCall[0];
226 }
227 });
228 mScrimController.finishAnimationsImmediately();
229 Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
230 Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
231 Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
232 }
233
234 @Test
235 public void testScrimCallbacksWithoutAmbientDisplay() {
236 mAlwaysOnEnabled = false;
237 testScrimCallback();
238 }
239
240 @Test
241 public void testScrimCallbackCancelled() {
242 boolean[] cancelledCalled = {false};
243 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
244 @Override
245 public void onCancelled() {
246 cancelledCalled[0] = true;
247 }
248 });
249 mScrimController.transitionTo(ScrimState.PULSING);
250 Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
251 }
252
253 @Test
254 public void testHoldsWakeLock() {
255 mScrimController.transitionTo(ScrimState.AOD);
256 verify(mWakeLock, times(1)).acquire();
257 verify(mWakeLock, never()).release();
258 mScrimController.finishAnimationsImmediately();
259 verify(mWakeLock, times(1)).release();
260 }
261
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800262 @Test
263 public void testCallbackInvokedOnSameStateTransition() {
264 mScrimController.transitionTo(ScrimState.UNLOCKED);
265 mScrimController.finishAnimationsImmediately();
266 ScrimController.Callback callback = mock(ScrimController.Callback.class);
267 mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
268 verify(callback, times(1)).onFinished();
269 }
270
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800271 private void assertScrimTint(ScrimView scrimView, boolean tinted) {
272 final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
273 final String name = scrimView == mScrimInFront ? "front" : "back";
274 Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
275 +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
276 tinted, viewIsTinted);
277 }
278
279 private void assertScrimVisibility(boolean inFront, boolean behind) {
280 Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
281 + mScrimInFront.getViewAlpha(), inFront, mScrimInFront.getViewAlpha() > 0);
282 Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
283 + mScrimBehind.getViewAlpha(), behind, mScrimBehind.getViewAlpha() > 0);
284 Assert.assertEquals("Invalid visibility.", inFront || behind, mScrimVisibile);
285 }
286
287 /**
288 * Special version of ScrimController where animations have 0 duration for test purposes.
289 */
290 private class SynchronousScrimController extends ScrimController {
291
292 private FakeHandler mHandler;
293
294 public SynchronousScrimController(LightBarController lightBarController,
295 ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
296 Consumer<Boolean> scrimVisibleListener, DozeParameters dozeParameters) {
297 super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
298 scrimVisibleListener, dozeParameters);
299 mHandler = new FakeHandler(Looper.myLooper());
300 }
301
302 public void finishAnimationsImmediately() {
303 boolean[] animationFinished = {false};
304 setOnAnimationFinished(()-> animationFinished[0] = true);
305
306 // Execute code that will trigger animations.
307 onPreDraw();
308
309 // Force finish screen blanking.
310 endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK);
311 mHandler.dispatchQueuedMessages();
312 // Force finish all animations.
313 endAnimation(mScrimBehind, TAG_KEY_ANIM);
314 endAnimation(mScrimInFront, TAG_KEY_ANIM);
315
316 if (!animationFinished[0]) {
317 throw new IllegalStateException("Animation never finished");
318 }
319 }
320
321 private void endAnimation(ScrimView scrimView, int tag) {
322 Animator animator = (Animator) scrimView.getTag(tag);
323 if (animator != null) {
324 animator.end();
325 }
326 }
327
328 @Override
329 protected Handler getHandler() {
330 return mHandler;
331 }
332
333 @Override
334 protected WakeLock createWakeLock() {
335 return mWakeLock;
336 }
337 }
338
339}