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