blob: 43e16dbeaeed9d49304b8564a5204bc8fa4ebcd2 [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;
Lucas Dupineea53b32017-12-18 13:47:14 -080030import static org.mockito.Mockito.verifyZeroInteractions;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080031import static org.mockito.Mockito.when;
32
33import android.animation.Animator;
Lucas Dupin82aa1632017-12-13 00:13:57 -080034import android.app.AlarmManager;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080035import android.graphics.Color;
36import android.os.Handler;
37import android.os.Looper;
38import android.support.test.filters.SmallTest;
39import android.testing.AndroidTestingRunner;
40import android.testing.TestableLooper;
Lucas Dupin8c7cb022018-02-05 10:49:03 -080041import android.view.Choreographer;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080042import android.view.View;
43
Lucas Dupin7517b5d2017-08-22 12:51:25 -070044import com.android.keyguard.KeyguardUpdateMonitor;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080045import com.android.systemui.SysuiTestCase;
46import com.android.systemui.statusbar.ScrimView;
47import com.android.systemui.util.wakelock.WakeLock;
48import com.android.systemui.utils.os.FakeHandler;
49
50import org.junit.Assert;
51import org.junit.Before;
52import org.junit.Test;
53import org.junit.runner.RunWith;
54
55import java.util.function.Consumer;
56
57@RunWith(AndroidTestingRunner.class)
58@TestableLooper.RunWithLooper
59@SmallTest
60public class ScrimControllerTest extends SysuiTestCase {
61
62 private SynchronousScrimController mScrimController;
63 private ScrimView mScrimBehind;
64 private ScrimView mScrimInFront;
65 private View mHeadsUpScrim;
Lucas Dupin82aa1632017-12-13 00:13:57 -080066 private Consumer<Integer> mScrimVisibilityCallback;
67 private int mScrimVisibility;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080068 private LightBarController mLightBarController;
69 private DozeParameters mDozeParamenters;
70 private WakeLock mWakeLock;
71 private boolean mAlwaysOnEnabled;
Lucas Dupin82aa1632017-12-13 00:13:57 -080072 private AlarmManager mAlarmManager;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080073
74 @Before
75 public void setup() {
76 mLightBarController = mock(LightBarController.class);
77 mScrimBehind = new ScrimView(getContext());
78 mScrimInFront = new ScrimView(getContext());
79 mHeadsUpScrim = mock(View.class);
80 mWakeLock = mock(WakeLock.class);
Lucas Dupin82aa1632017-12-13 00:13:57 -080081 mAlarmManager = mock(AlarmManager.class);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080082 mAlwaysOnEnabled = true;
Lucas Dupin82aa1632017-12-13 00:13:57 -080083 mScrimVisibilityCallback = (Integer visible) -> mScrimVisibility = visible;
Lucas Dupin9e3fa102017-11-08 17:16:55 -080084 mDozeParamenters = mock(DozeParameters.class);
85 when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
Lucas Dupin43d0d732017-11-16 11:23:49 -080086 when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080087 mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
Lucas Dupin82aa1632017-12-13 00:13:57 -080088 mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
89 mAlarmManager);
Lucas Dupin9e3fa102017-11-08 17:16:55 -080090 }
91
92 @Test
93 public void initialState() {
94 Assert.assertEquals("ScrimController should start initialized",
95 mScrimController.getState(), ScrimState.UNINITIALIZED);
96 }
97
98 @Test
99 public void transitionToKeyguard() {
100 mScrimController.transitionTo(ScrimState.KEYGUARD);
101 mScrimController.finishAnimationsImmediately();
102 // Front scrim should be transparent
103 // Back scrim should be visible without tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800104 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800105 assertScrimTint(mScrimBehind, false /* tinted */);
106 }
107
108 @Test
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700109 public void transitionToAod_withRegularWallpaper() {
110 mScrimController.transitionTo(ScrimState.AOD);
111 mScrimController.finishAnimationsImmediately();
112 // Front scrim should be transparent
113 // Back scrim should be visible with tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800114 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700115 assertScrimTint(mScrimBehind, true /* tinted */);
116 assertScrimTint(mScrimInFront, true /* tinted */);
117 }
118
119 @Test
120 public void transitionToAod_withAodWallpaper() {
121 mScrimController.setWallpaperSupportsAmbientMode(true);
122 mScrimController.transitionTo(ScrimState.AOD);
123 mScrimController.finishAnimationsImmediately();
124 // Front scrim should be transparent
125 // Back scrim should be transparent
Lucas Dupin82aa1632017-12-13 00:13:57 -0800126 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
127
128 // Move on to PULSING and check if the back scrim is still transparent
129 mScrimController.transitionTo(ScrimState.PULSING);
130 mScrimController.finishAnimationsImmediately();
131 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin7517b5d2017-08-22 12:51:25 -0700132 }
133
134 @Test
135 public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
136 ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
137 @Override
138 public boolean hasLockscreenWallpaper() {
139 return true;
140 }
141 };
142 mScrimController.setWallpaperSupportsAmbientMode(true);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800143 mScrimController.transitionTo(ScrimState.AOD);
144 mScrimController.finishAnimationsImmediately();
145 // Front scrim should be transparent
146 // Back scrim should be visible with tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800147 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800148 assertScrimTint(mScrimBehind, true /* tinted */);
149 assertScrimTint(mScrimInFront, true /* tinted */);
150 }
151
152 @Test
153 public void transitionToPulsing() {
Lucas Dupin82aa1632017-12-13 00:13:57 -0800154 // Pre-condition
155 // Need to go to AoD first because PULSING doesn't change
156 // the back scrim opacity - otherwise it would hide AoD wallpapers.
157 mScrimController.setWallpaperSupportsAmbientMode(false);
158 mScrimController.transitionTo(ScrimState.AOD);
159 mScrimController.finishAnimationsImmediately();
160 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
161
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800162 mScrimController.transitionTo(ScrimState.PULSING);
163 mScrimController.finishAnimationsImmediately();
164 // Front scrim should be transparent
165 // Back scrim should be visible with tint
166 // Pulse callback should have been invoked
Lucas Dupin82aa1632017-12-13 00:13:57 -0800167 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800168 assertScrimTint(mScrimBehind, true /* tinted */);
169 }
170
171 @Test
172 public void transitionToBouncer() {
173 mScrimController.transitionTo(ScrimState.BOUNCER);
174 mScrimController.finishAnimationsImmediately();
175 // Front scrim should be transparent
176 // Back scrim should be visible without tint
Lucas Dupin82aa1632017-12-13 00:13:57 -0800177 assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800178 assertScrimTint(mScrimBehind, false /* tinted */);
179 }
180
181 @Test
182 public void transitionToUnlocked() {
183 mScrimController.transitionTo(ScrimState.UNLOCKED);
184 mScrimController.finishAnimationsImmediately();
185 // Front scrim should be transparent
186 // Back scrim should be transparent
Lucas Dupin82aa1632017-12-13 00:13:57 -0800187 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800188 assertScrimTint(mScrimBehind, false /* tinted */);
189 assertScrimTint(mScrimInFront, false /* tinted */);
190
191 // Back scrim should be visible after start dragging
192 mScrimController.setPanelExpansion(0.5f);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800193 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800194 }
195
196 @Test
197 public void transitionToUnlockedFromAod() {
198 // Simulate unlock with fingerprint
199 mScrimController.transitionTo(ScrimState.AOD);
200 mScrimController.finishAnimationsImmediately();
201 mScrimController.transitionTo(ScrimState.UNLOCKED);
202 // Immediately tinted after the transition starts
203 assertScrimTint(mScrimInFront, true /* tinted */);
204 assertScrimTint(mScrimBehind, true /* tinted */);
205 mScrimController.finishAnimationsImmediately();
206 // Front scrim should be transparent
207 // Back scrim should be transparent
208 // Neither scrims should be tinted anymore after the animation.
Lucas Dupin82aa1632017-12-13 00:13:57 -0800209 assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800210 assertScrimTint(mScrimInFront, false /* tinted */);
211 assertScrimTint(mScrimBehind, false /* tinted */);
212 }
213
214 @Test
215 public void scrimBlanksBeforeLeavingAoD() {
216 // Simulate unlock with fingerprint
217 mScrimController.transitionTo(ScrimState.AOD);
218 mScrimController.finishAnimationsImmediately();
219 mScrimController.transitionTo(ScrimState.UNLOCKED,
220 new ScrimController.Callback() {
221 @Override
222 public void onDisplayBlanked() {
223 // Front scrim should be black in the middle of the transition
224 Assert.assertTrue("Scrim should be visible during transition. Alpha: "
225 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
226 assertScrimTint(mScrimInFront, true /* tinted */);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800227 Assert.assertSame("Scrim should be visible during transition.",
228 mScrimVisibility, VISIBILITY_FULLY_OPAQUE);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800229 }
230 });
231 mScrimController.finishAnimationsImmediately();
232 }
233
234 @Test
235 public void testScrimCallback() {
236 int[] callOrder = {0, 0, 0};
237 int[] currentCall = {0};
238 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
239 @Override
240 public void onStart() {
241 callOrder[0] = ++currentCall[0];
242 }
243
244 @Override
245 public void onDisplayBlanked() {
246 callOrder[1] = ++currentCall[0];
247 }
248
249 @Override
250 public void onFinished() {
251 callOrder[2] = ++currentCall[0];
252 }
253 });
254 mScrimController.finishAnimationsImmediately();
255 Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
256 Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
257 Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
258 }
259
260 @Test
261 public void testScrimCallbacksWithoutAmbientDisplay() {
262 mAlwaysOnEnabled = false;
263 testScrimCallback();
264 }
265
266 @Test
267 public void testScrimCallbackCancelled() {
268 boolean[] cancelledCalled = {false};
269 mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
270 @Override
271 public void onCancelled() {
272 cancelledCalled[0] = true;
273 }
274 });
275 mScrimController.transitionTo(ScrimState.PULSING);
276 Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
277 }
278
279 @Test
Lucas Dupineea53b32017-12-18 13:47:14 -0800280 public void testHoldsWakeLock_whenAOD() {
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800281 mScrimController.transitionTo(ScrimState.AOD);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800282 verify(mWakeLock).acquire();
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800283 verify(mWakeLock, never()).release();
284 mScrimController.finishAnimationsImmediately();
Lucas Dupin82aa1632017-12-13 00:13:57 -0800285 verify(mWakeLock).release();
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800286 }
287
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800288 @Test
Lucas Dupineea53b32017-12-18 13:47:14 -0800289 public void testDoesNotHoldWakeLock_whenUnlocking() {
290 mScrimController.transitionTo(ScrimState.UNLOCKED);
291 mScrimController.finishAnimationsImmediately();
292 verifyZeroInteractions(mWakeLock);
293 }
294
295 @Test
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800296 public void testCallbackInvokedOnSameStateTransition() {
297 mScrimController.transitionTo(ScrimState.UNLOCKED);
298 mScrimController.finishAnimationsImmediately();
299 ScrimController.Callback callback = mock(ScrimController.Callback.class);
300 mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
Lucas Dupin82aa1632017-12-13 00:13:57 -0800301 verify(callback).onFinished();
302 }
303
304 @Test
305 public void testHoldsAodWallpaperAnimationLock() {
306 // Pre-conditions
307 mScrimController.transitionTo(ScrimState.AOD);
308 mScrimController.finishAnimationsImmediately();
309 reset(mWakeLock);
310
311 mScrimController.onHideWallpaperTimeout();
312 verify(mWakeLock).acquire();
313 verify(mWakeLock, never()).release();
314 mScrimController.finishAnimationsImmediately();
315 verify(mWakeLock).release();
316 }
317
318 @Test
319 public void testWillHideAoDWallpaper() {
320 mScrimController.setWallpaperSupportsAmbientMode(true);
321 mScrimController.transitionTo(ScrimState.AOD);
322 verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
323 mScrimController.transitionTo(ScrimState.KEYGUARD);
324 verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
Lucas Dupin19aba8e2017-12-11 12:42:26 -0800325 }
326
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800327 private void assertScrimTint(ScrimView scrimView, boolean tinted) {
328 final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
329 final String name = scrimView == mScrimInFront ? "front" : "back";
330 Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
331 +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
332 tinted, viewIsTinted);
333 }
334
Lucas Dupin82aa1632017-12-13 00:13:57 -0800335 private void assertScrimVisibility(int inFront, int behind) {
336 boolean inFrontVisible = inFront != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
337 boolean behindVisible = behind != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800338 Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
Lucas Dupin82aa1632017-12-13 00:13:57 -0800339 + mScrimInFront.getViewAlpha(), inFrontVisible, mScrimInFront.getViewAlpha() > 0);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800340 Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
Lucas Dupin82aa1632017-12-13 00:13:57 -0800341 + mScrimBehind.getViewAlpha(), behindVisible, mScrimBehind.getViewAlpha() > 0);
342
343 final int visibility;
344 if (inFront == VISIBILITY_FULLY_OPAQUE || behind == VISIBILITY_FULLY_OPAQUE) {
345 visibility = VISIBILITY_FULLY_OPAQUE;
346 } else if (inFront > VISIBILITY_FULLY_TRANSPARENT || behind > VISIBILITY_FULLY_TRANSPARENT) {
347 visibility = VISIBILITY_SEMI_TRANSPARENT;
348 } else {
349 visibility = VISIBILITY_FULLY_TRANSPARENT;
350 }
351 Assert.assertEquals("Invalid visibility.", visibility, mScrimVisibility);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800352 }
353
354 /**
355 * Special version of ScrimController where animations have 0 duration for test purposes.
356 */
357 private class SynchronousScrimController extends ScrimController {
358
359 private FakeHandler mHandler;
360
361 public SynchronousScrimController(LightBarController lightBarController,
362 ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
Lucas Dupin82aa1632017-12-13 00:13:57 -0800363 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
364 AlarmManager alarmManager) {
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800365 super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
Lucas Dupin82aa1632017-12-13 00:13:57 -0800366 scrimVisibleListener, dozeParameters, alarmManager);
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800367 mHandler = new FakeHandler(Looper.myLooper());
368 }
369
370 public void finishAnimationsImmediately() {
371 boolean[] animationFinished = {false};
372 setOnAnimationFinished(()-> animationFinished[0] = true);
373
374 // Execute code that will trigger animations.
375 onPreDraw();
376
377 // Force finish screen blanking.
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800378 mHandler.dispatchQueuedMessages();
379 // Force finish all animations.
380 endAnimation(mScrimBehind, TAG_KEY_ANIM);
381 endAnimation(mScrimInFront, TAG_KEY_ANIM);
382
383 if (!animationFinished[0]) {
384 throw new IllegalStateException("Animation never finished");
385 }
386 }
387
388 private void endAnimation(ScrimView scrimView, int tag) {
389 Animator animator = (Animator) scrimView.getTag(tag);
390 if (animator != null) {
391 animator.end();
392 }
393 }
394
395 @Override
396 protected Handler getHandler() {
397 return mHandler;
398 }
399
400 @Override
401 protected WakeLock createWakeLock() {
402 return mWakeLock;
403 }
Lucas Dupin8c7cb022018-02-05 10:49:03 -0800404
405 /**
406 * Do not wait for a frame since we're in a test environment.
407 * @param callback What to execute.
408 */
409 @Override
410 protected void doOnTheNextFrame(Choreographer.FrameCallback callback) {
411 callback.doFrame(0);
412 }
Lucas Dupin9e3fa102017-11-08 17:16:55 -0800413 }
414
415}