blob: 9b7efe9949ad2432d4e2998525d0fe7c006d5e09 [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
Lucas Dupin82aa1632017-12-13 00:13:57 -080019import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_OPAQUE;
20import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_TRANSPARENT;
21import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_SEMI_TRANSPARENT;
22
23import static org.mockito.ArgumentMatchers.any;
24import static org.mockito.ArgumentMatchers.anyInt;
25import static org.mockito.ArgumentMatchers.anyLong;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080026import static org.mockito.Mockito.mock;
27import static org.mockito.Mockito.never;
Lucas Dupin82aa1632017-12-13 00:13:57 -080028import static org.mockito.Mockito.reset;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080029import static org.mockito.Mockito.verify;
30import static org.mockito.Mockito.when;
31
32import android.animation.Animator;
Lucas Dupin82aa1632017-12-13 00:13:57 -080033import android.app.AlarmManager;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080034import android.graphics.Color;
35import android.os.Handler;
36import android.os.Looper;
37import android.support.test.filters.SmallTest;
38import android.testing.AndroidTestingRunner;
39import android.testing.TestableLooper;
40import android.view.View;
41
Lucas Dupin7517b5d2017-08-22 12:51:25 -070042import com.android.keyguard.KeyguardUpdateMonitor;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080043import com.android.systemui.SysuiTestCase;
44import com.android.systemui.statusbar.ScrimView;
45import com.android.systemui.util.wakelock.WakeLock;
46import com.android.systemui.utils.os.FakeHandler;
47
48import org.junit.Assert;
49import org.junit.Before;
50import org.junit.Test;
51import org.junit.runner.RunWith;
52
53import java.util.function.Consumer;
54
55@RunWith(AndroidTestingRunner.class)
56@TestableLooper.RunWithLooper
57@SmallTest
58public class ScrimControllerTest extends SysuiTestCase {
59
60 private SynchronousScrimController mScrimController;
61 private ScrimView mScrimBehind;
62 private ScrimView mScrimInFront;
63 private View mHeadsUpScrim;
Lucas Dupin82aa1632017-12-13 00:13:57 -080064 private Consumer<Integer> mScrimVisibilityCallback;
65 private int mScrimVisibility;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080066 private LightBarController mLightBarController;
67 private DozeParameters mDozeParamenters;
68 private WakeLock mWakeLock;
69 private boolean mAlwaysOnEnabled;
Lucas Dupin82aa1632017-12-13 00:13:57 -080070 private AlarmManager mAlarmManager;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080071
72 @Before
73 public void setup() {
74 mLightBarController = mock(LightBarController.class);
75 mScrimBehind = new ScrimView(getContext());
76 mScrimInFront = new ScrimView(getContext());
77 mHeadsUpScrim = mock(View.class);
78 mWakeLock = mock(WakeLock.class);
Lucas Dupin82aa1632017-12-13 00:13:57 -080079 mAlarmManager = mock(AlarmManager.class);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080080 mAlwaysOnEnabled = true;
Lucas Dupin82aa1632017-12-13 00:13:57 -080081 mScrimVisibilityCallback = (Integer visible) -> mScrimVisibility = visible;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080082 mDozeParamenters = mock(DozeParameters.class);
83 when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
Lucas Dupin43d0d732017-11-16 11:23:49 -080084 when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080085 mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
Lucas Dupin82aa1632017-12-13 00:13:57 -080086 mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
87 mAlarmManager);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080088 }
89
90 @Test
91 public void initialState() {
92 Assert.assertEquals("ScrimController should start initialized",
93 mScrimController.getState(), ScrimState.UNINITIALIZED);
94 }
95
96 @Test
97 public void transitionToKeyguard() {
98 mScrimController.transitionTo(ScrimState.KEYGUARD);
99 mScrimController.finishAnimationsImmediately();
100 // Front scrim should be transparent
101 // Back scrim should be visible without tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800102 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800103 assertScrimTint(mScrimBehind, false /* tinted */);
104 }
105
106 @Test
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700107 public void transitionToAod_withRegularWallpaper() {
108 mScrimController.transitionTo(ScrimState.AOD);
109 mScrimController.finishAnimationsImmediately();
110 // Front scrim should be transparent
111 // Back scrim should be visible with tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800112 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700113 assertScrimTint(mScrimBehind, true /* tinted */);
114 assertScrimTint(mScrimInFront, true /* tinted */);
115 }
116
117 @Test
118 public void transitionToAod_withAodWallpaper() {
119 mScrimController.setWallpaperSupportsAmbientMode(true);
120 mScrimController.transitionTo(ScrimState.AOD);
121 mScrimController.finishAnimationsImmediately();
122 // Front scrim should be transparent
123 // Back scrim should be transparent
Lucas Dupin82aa1632017-12-13 00:13:57 -0800124 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
125
126 // Move on to PULSING and check if the back scrim is still transparent
127 mScrimController.transitionTo(ScrimState.PULSING);
128 mScrimController.finishAnimationsImmediately();
129 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700130 }
131
132 @Test
133 public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
134 ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
135 @Override
136 public boolean hasLockscreenWallpaper() {
137 return true;
138 }
139 };
140 mScrimController.setWallpaperSupportsAmbientMode(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800141 mScrimController.transitionTo(ScrimState.AOD);
142 mScrimController.finishAnimationsImmediately();
143 // Front scrim should be transparent
144 // Back scrim should be visible with tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800145 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800146 assertScrimTint(mScrimBehind, true /* tinted */);
147 assertScrimTint(mScrimInFront, true /* tinted */);
148 }
149
150 @Test
151 public void transitionToPulsing() {
Lucas Dupin82aa1632017-12-13 00:13:57 -0800152 // Pre-condition
153 // Need to go to AoD first because PULSING doesn't change
154 // the back scrim opacity - otherwise it would hide AoD wallpapers.
155 mScrimController.setWallpaperSupportsAmbientMode(false);
156 mScrimController.transitionTo(ScrimState.AOD);
157 mScrimController.finishAnimationsImmediately();
158 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
159
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800160 mScrimController.transitionTo(ScrimState.PULSING);
161 mScrimController.finishAnimationsImmediately();
162 // Front scrim should be transparent
163 // Back scrim should be visible with tint
164 // Pulse callback should have been invoked
Lucas Dupin82aa1632017-12-13 00:13:57 -0800165 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800166 assertScrimTint(mScrimBehind, true /* tinted */);
167 }
168
169 @Test
170 public void transitionToBouncer() {
171 mScrimController.transitionTo(ScrimState.BOUNCER);
172 mScrimController.finishAnimationsImmediately();
173 // Front scrim should be transparent
174 // Back scrim should be visible without tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800175 assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800176 assertScrimTint(mScrimBehind, false /* tinted */);
177 }
178
179 @Test
180 public void transitionToUnlocked() {
181 mScrimController.transitionTo(ScrimState.UNLOCKED);
182 mScrimController.finishAnimationsImmediately();
183 // Front scrim should be transparent
184 // Back scrim should be transparent
Lucas Dupin82aa1632017-12-13 00:13:57 -0800185 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800186 assertScrimTint(mScrimBehind, false /* tinted */);
187 assertScrimTint(mScrimInFront, false /* tinted */);
188
189 // Back scrim should be visible after start dragging
190 mScrimController.setPanelExpansion(0.5f);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800191 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800192 }
193
194 @Test
195 public void transitionToUnlockedFromAod() {
196 // Simulate unlock with fingerprint
197 mScrimController.transitionTo(ScrimState.AOD);
198 mScrimController.finishAnimationsImmediately();
199 mScrimController.transitionTo(ScrimState.UNLOCKED);
200 // Immediately tinted after the transition starts
201 assertScrimTint(mScrimInFront, true /* tinted */);
202 assertScrimTint(mScrimBehind, true /* tinted */);
203 mScrimController.finishAnimationsImmediately();
204 // Front scrim should be transparent
205 // Back scrim should be transparent
206 // Neither scrims should be tinted anymore after the animation.
Lucas Dupin82aa1632017-12-13 00:13:57 -0800207 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800208 assertScrimTint(mScrimInFront, false /* tinted */);
209 assertScrimTint(mScrimBehind, false /* tinted */);
210 }
211
212 @Test
213 public void scrimBlanksBeforeLeavingAoD() {
214 // Simulate unlock with fingerprint
215 mScrimController.transitionTo(ScrimState.AOD);
216 mScrimController.finishAnimationsImmediately();
217 mScrimController.transitionTo(ScrimState.UNLOCKED,
218 new ScrimController.Callback() {
219 @Override
220 public void onDisplayBlanked() {
221 // Front scrim should be black in the middle of the transition
222 Assert.assertTrue("Scrim should be visible during transition. Alpha: "
223 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
224 assertScrimTint(mScrimInFront, true /* tinted */);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800225 Assert.assertSame("Scrim should be visible during transition.",
226 mScrimVisibility, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800227 }
228 });
229 mScrimController.finishAnimationsImmediately();
230 }
231
232 @Test
233 public void testScrimCallback() {
234 int[] callOrder = {0, 0, 0};
235 int[] currentCall = {0};
236 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
237 @Override
238 public void onStart() {
239 callOrder[0] = ++currentCall[0];
240 }
241
242 @Override
243 public void onDisplayBlanked() {
244 callOrder[1] = ++currentCall[0];
245 }
246
247 @Override
248 public void onFinished() {
249 callOrder[2] = ++currentCall[0];
250 }
251 });
252 mScrimController.finishAnimationsImmediately();
253 Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
254 Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
255 Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
256 }
257
258 @Test
259 public void testScrimCallbacksWithoutAmbientDisplay() {
260 mAlwaysOnEnabled = false;
261 testScrimCallback();
262 }
263
264 @Test
265 public void testScrimCallbackCancelled() {
266 boolean[] cancelledCalled = {false};
267 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
268 @Override
269 public void onCancelled() {
270 cancelledCalled[0] = true;
271 }
272 });
273 mScrimController.transitionTo(ScrimState.PULSING);
274 Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
275 }
276
277 @Test
278 public void testHoldsWakeLock() {
279 mScrimController.transitionTo(ScrimState.AOD);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800280 verify(mWakeLock).acquire();
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800281 verify(mWakeLock, never()).release();
282 mScrimController.finishAnimationsImmediately();
Lucas Dupin82aa1632017-12-13 00:13:57 -0800283 verify(mWakeLock).release();
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800284 }
285
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800286 @Test
287 public void testCallbackInvokedOnSameStateTransition() {
288 mScrimController.transitionTo(ScrimState.UNLOCKED);
289 mScrimController.finishAnimationsImmediately();
290 ScrimController.Callback callback = mock(ScrimController.Callback.class);
291 mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800292 verify(callback).onFinished();
293 }
294
295 @Test
296 public void testHoldsAodWallpaperAnimationLock() {
297 // Pre-conditions
298 mScrimController.transitionTo(ScrimState.AOD);
299 mScrimController.finishAnimationsImmediately();
300 reset(mWakeLock);
301
302 mScrimController.onHideWallpaperTimeout();
303 verify(mWakeLock).acquire();
304 verify(mWakeLock, never()).release();
305 mScrimController.finishAnimationsImmediately();
306 verify(mWakeLock).release();
307 }
308
309 @Test
310 public void testWillHideAoDWallpaper() {
311 mScrimController.setWallpaperSupportsAmbientMode(true);
312 mScrimController.transitionTo(ScrimState.AOD);
313 verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
314 mScrimController.transitionTo(ScrimState.KEYGUARD);
315 verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800316 }
317
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800318 private void assertScrimTint(ScrimView scrimView, boolean tinted) {
319 final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
320 final String name = scrimView == mScrimInFront ? "front" : "back";
321 Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
322 +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
323 tinted, viewIsTinted);
324 }
325
Lucas Dupin82aa1632017-12-13 00:13:57 -0800326 private void assertScrimVisibility(int inFront, int behind) {
327 boolean inFrontVisible = inFront != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
328 boolean behindVisible = behind != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800329 Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
Lucas Dupin82aa1632017-12-13 00:13:57 -0800330 + mScrimInFront.getViewAlpha(), inFrontVisible, mScrimInFront.getViewAlpha() > 0);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800331 Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
Lucas Dupin82aa1632017-12-13 00:13:57 -0800332 + mScrimBehind.getViewAlpha(), behindVisible, mScrimBehind.getViewAlpha() > 0);
333
334 final int visibility;
335 if (inFront == VISIBILITY_FULLY_OPAQUE || behind == VISIBILITY_FULLY_OPAQUE) {
336 visibility = VISIBILITY_FULLY_OPAQUE;
337 } else if (inFront > VISIBILITY_FULLY_TRANSPARENT || behind > VISIBILITY_FULLY_TRANSPARENT) {
338 visibility = VISIBILITY_SEMI_TRANSPARENT;
339 } else {
340 visibility = VISIBILITY_FULLY_TRANSPARENT;
341 }
342 Assert.assertEquals("Invalid visibility.", visibility, mScrimVisibility);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800343 }
344
345 /**
346 * Special version of ScrimController where animations have 0 duration for test purposes.
347 */
348 private class SynchronousScrimController extends ScrimController {
349
350 private FakeHandler mHandler;
351
352 public SynchronousScrimController(LightBarController lightBarController,
353 ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
Lucas Dupin82aa1632017-12-13 00:13:57 -0800354 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
355 AlarmManager alarmManager) {
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800356 super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
Lucas Dupin82aa1632017-12-13 00:13:57 -0800357 scrimVisibleListener, dozeParameters, alarmManager);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800358 mHandler = new FakeHandler(Looper.myLooper());
359 }
360
361 public void finishAnimationsImmediately() {
362 boolean[] animationFinished = {false};
363 setOnAnimationFinished(()-> animationFinished[0] = true);
364
365 // Execute code that will trigger animations.
366 onPreDraw();
367
368 // Force finish screen blanking.
369 endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK);
370 mHandler.dispatchQueuedMessages();
371 // Force finish all animations.
372 endAnimation(mScrimBehind, TAG_KEY_ANIM);
373 endAnimation(mScrimInFront, TAG_KEY_ANIM);
374
375 if (!animationFinished[0]) {
376 throw new IllegalStateException("Animation never finished");
377 }
378 }
379
380 private void endAnimation(ScrimView scrimView, int tag) {
381 Animator animator = (Animator) scrimView.getTag(tag);
382 if (animator != null) {
383 animator.end();
384 }
385 }
386
387 @Override
388 protected Handler getHandler() {
389 return mHandler;
390 }
391
392 @Override
393 protected WakeLock createWakeLock() {
394 return mWakeLock;
395 }
396 }
397
398}