1 /*
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 
17 package com.android.systemui.statusbar.phone;
18 
19 import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
20 import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
21 import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
22 import static com.android.systemui.statusbar.phone.ScrimState.BOUNCER;
23 import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.anyFloat;
31 import static org.mockito.ArgumentMatchers.anyInt;
32 import static org.mockito.ArgumentMatchers.anyLong;
33 import static org.mockito.ArgumentMatchers.anyString;
34 import static org.mockito.Mockito.doAnswer;
35 import static org.mockito.Mockito.mock;
36 import static org.mockito.Mockito.never;
37 import static org.mockito.Mockito.reset;
38 import static org.mockito.Mockito.spy;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.verifyZeroInteractions;
41 import static org.mockito.Mockito.when;
42 
43 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
44 
45 import android.animation.Animator;
46 import android.app.AlarmManager;
47 import android.content.Context;
48 import android.content.res.ColorStateList;
49 import android.content.res.TypedArray;
50 import android.graphics.Color;
51 import android.os.Handler;
52 import android.testing.AndroidTestingRunner;
53 import android.testing.TestableLooper;
54 import android.util.MathUtils;
55 import android.view.View;
56 
57 import androidx.test.filters.SmallTest;
58 
59 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
60 import com.android.keyguard.BouncerPanelExpansionCalculator;
61 import com.android.keyguard.KeyguardUpdateMonitor;
62 import com.android.keyguard.TestScopeProvider;
63 import com.android.systemui.DejankUtils;
64 import com.android.systemui.SysuiTestCase;
65 import com.android.systemui.animation.ShadeInterpolation;
66 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
67 import com.android.systemui.dock.DockManager;
68 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
69 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
70 import com.android.systemui.keyguard.shared.model.KeyguardState;
71 import com.android.systemui.keyguard.shared.model.TransitionState;
72 import com.android.systemui.keyguard.shared.model.TransitionStep;
73 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
74 import com.android.systemui.scrim.ScrimView;
75 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
76 import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator;
77 import com.android.systemui.statusbar.policy.FakeConfigurationController;
78 import com.android.systemui.statusbar.policy.KeyguardStateController;
79 import com.android.systemui.util.concurrency.FakeExecutor;
80 import com.android.systemui.util.kotlin.JavaAdapter;
81 import com.android.systemui.util.time.FakeSystemClock;
82 import com.android.systemui.util.wakelock.DelayedWakeLock;
83 import com.android.systemui.utils.os.FakeHandler;
84 import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
85 
86 import com.google.common.truth.Expect;
87 
88 import org.junit.After;
89 import org.junit.Assert;
90 import org.junit.Before;
91 import org.junit.Rule;
92 import org.junit.Test;
93 import org.junit.runner.RunWith;
94 import org.mockito.Mock;
95 import org.mockito.MockitoAnnotations;
96 import org.mockito.stubbing.Answer;
97 
98 import java.util.Arrays;
99 import java.util.Collections;
100 import java.util.HashMap;
101 import java.util.HashSet;
102 import java.util.Map;
103 
104 import kotlinx.coroutines.CoroutineDispatcher;
105 import kotlinx.coroutines.test.TestScope;
106 
107 @RunWith(AndroidTestingRunner.class)
108 @TestableLooper.RunWithLooper(setAsMainLooper = true)
109 @SmallTest
110 public class ScrimControllerTest extends SysuiTestCase {
111 
112     @Rule public Expect mExpect = Expect.create();
113 
114     private final FakeConfigurationController mConfigurationController =
115             new FakeConfigurationController();
116     private final LargeScreenShadeInterpolator
117             mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator();
118 
119     private final TestScope mTestScope = TestScopeProvider.getTestScope();
120     private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
121 
122     private ScrimController mScrimController;
123     private ScrimView mScrimBehind;
124     private ScrimView mNotificationsScrim;
125     private ScrimView mScrimInFront;
126     private ScrimState mScrimState;
127     private float mScrimBehindAlpha;
128     private GradientColors mScrimInFrontColor;
129     private int mScrimVisibility;
130     private boolean mAlwaysOnEnabled;
131     private TestableLooper mLooper;
132     private Context mContext;
133     @Mock private AlarmManager mAlarmManager;
134     @Mock private DozeParameters mDozeParameters;
135     @Mock private LightBarController mLightBarController;
136     @Mock private DelayedWakeLock.Builder mDelayedWakeLockBuilder;
137     @Mock private DelayedWakeLock mWakeLock;
138     @Mock private KeyguardStateController mKeyguardStateController;
139     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
140     @Mock private DockManager mDockManager;
141     @Mock private ScreenOffAnimationController mScreenOffAnimationController;
142     @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
143     @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
144     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
145     private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
146     @Mock private CoroutineDispatcher mMainDispatcher;
147     @Mock private TypedArray mMockTypedArray;
148 
149     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
150     //   event-dispatch-on-registration pattern caused some of these unit tests to fail.)
151     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
152 
153     private static class AnimatorListener implements Animator.AnimatorListener {
154         private int mNumStarts;
155         private int mNumEnds;
156         private int mNumCancels;
157 
158         @Override
onAnimationStart(Animator animation)159         public void onAnimationStart(Animator animation) {
160             mNumStarts++;
161         }
162 
163         @Override
onAnimationEnd(Animator animation)164         public void onAnimationEnd(Animator animation) {
165             mNumEnds++;
166         }
167 
168         @Override
onAnimationCancel(Animator animation)169         public void onAnimationCancel(Animator animation) {
170             mNumCancels++;
171         }
172 
173         @Override
onAnimationRepeat(Animator animation)174         public void onAnimationRepeat(Animator animation) {
175 
176         }
177 
getNumStarts()178         public int getNumStarts() {
179             return mNumStarts;
180         }
181 
getNumEnds()182         public int getNumEnds() {
183             return mNumEnds;
184         }
185 
getNumCancels()186         public int getNumCancels() {
187             return mNumCancels;
188         }
189 
reset()190         public void reset() {
191             mNumStarts = 0;
192             mNumEnds = 0;
193             mNumCancels = 0;
194         }
195     }
196 
197     private AnimatorListener mAnimatorListener = new AnimatorListener();
198 
199     private int mSurfaceColor = 0x112233;
200 
finishAnimationsImmediately()201     private void finishAnimationsImmediately() {
202         // Execute code that will trigger animations.
203         mScrimController.onPreDraw();
204         // Force finish all animations.
205         mLooper.processAllMessages();
206         endAnimation(mNotificationsScrim);
207         endAnimation(mScrimBehind);
208         endAnimation(mScrimInFront);
209 
210         assertEquals("Animators did not finish",
211                 mAnimatorListener.getNumStarts(), mAnimatorListener.getNumEnds());
212     }
213 
endAnimation(View scrimView)214     private void endAnimation(View scrimView) {
215         Animator animator = getAnimator(scrimView);
216         if (animator != null) {
217             animator.end();
218         }
219     }
220 
getAnimator(View scrimView)221     private Animator getAnimator(View scrimView) {
222         return (Animator) scrimView.getTag(ScrimController.TAG_KEY_ANIM);
223     }
224 
225     @Before
setup()226     public void setup() {
227         MockitoAnnotations.initMocks(this);
228         mContext = spy(getContext());
229         when(mContext.obtainStyledAttributes(
230                 new int[]{com.android.internal.R.attr.materialColorSurface}))
231                 .thenReturn(mMockTypedArray);
232 
233         when(mMockTypedArray.getColorStateList(anyInt()))
234                 .thenAnswer((invocation) -> ColorStateList.valueOf(mSurfaceColor));
235 
236         mScrimBehind = spy(new ScrimView(mContext));
237         mScrimInFront = new ScrimView(mContext);
238         mNotificationsScrim = new ScrimView(mContext);
239         mAlwaysOnEnabled = true;
240         mLooper = TestableLooper.get(this);
241         DejankUtils.setImmediate(true);
242 
243         // ScrimController uses mScrimBehind to delay some callbacks that we should run immediately.
244         doAnswer(invocation -> {
245             ((Runnable) invocation.getArgument(0)).run();
246             return null;
247         }).when(mScrimBehind).postOnAnimationDelayed(any(Runnable.class), anyLong());
248 
249         when(mDozeParameters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
250         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
251 
252         doAnswer((Answer<Void>) invocation -> {
253             mScrimState = invocation.getArgument(0);
254             mScrimBehindAlpha = invocation.getArgument(1);
255             mScrimInFrontColor = invocation.getArgument(2);
256             return null;
257         }).when(mLightBarController).setScrimState(
258                 any(ScrimState.class), anyFloat(), any(GradientColors.class));
259 
260         when(mDelayedWakeLockBuilder.setHandler(any(Handler.class)))
261                 .thenReturn(mDelayedWakeLockBuilder);
262         when(mDelayedWakeLockBuilder.setTag(any(String.class)))
263                 .thenReturn(mDelayedWakeLockBuilder);
264         when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
265         when(mDockManager.isDocked()).thenReturn(false);
266 
267         when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
268                 .thenReturn(emptyFlow());
269         when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha())
270                 .thenReturn(emptyFlow());
271 
272         mScrimController = new ScrimController(
273                 mLightBarController,
274                 mDozeParameters,
275                 mAlarmManager,
276                 mKeyguardStateController,
277                 mDelayedWakeLockBuilder,
278                 new FakeHandler(mLooper.getLooper()),
279                 mKeyguardUpdateMonitor,
280                 mDockManager,
281                 mConfigurationController,
282                 new FakeExecutor(new FakeSystemClock()),
283                 mJavaAdapter,
284                 mScreenOffAnimationController,
285                 mKeyguardUnlockAnimationController,
286                 mStatusBarKeyguardViewManager,
287                 mPrimaryBouncerToGoneTransitionViewModel,
288                 mKeyguardTransitionInteractor,
289                 mWallpaperRepository,
290                 mMainDispatcher,
291                 mLinearLargeScreenShadeInterpolator);
292         mScrimController.start();
293         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
294         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
295         mScrimController.setAnimatorListener(mAnimatorListener);
296 
297         mScrimController.setHasBackdrop(false);
298 
299         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
300         mTestScope.getTestScheduler().runCurrent();
301 
302         mScrimController.transitionTo(ScrimState.KEYGUARD);
303         finishAnimationsImmediately();
304     }
305 
306     @After
tearDown()307     public void tearDown() {
308         finishAnimationsImmediately();
309         Arrays.stream(ScrimState.values()).forEach((scrim) -> {
310             scrim.setAodFrontScrimAlpha(0f);
311             scrim.setClipQsScrim(false);
312         });
313         DejankUtils.setImmediate(false);
314     }
315 
316     @Test
transitionToKeyguard()317     public void transitionToKeyguard() {
318         mScrimController.transitionTo(ScrimState.KEYGUARD);
319         finishAnimationsImmediately();
320 
321         assertScrimAlpha(Map.of(
322                 mScrimInFront, TRANSPARENT,
323                 mScrimBehind, SEMI_TRANSPARENT));
324 
325         assertScrimTinted(Map.of(
326                 mScrimInFront, true,
327                 mScrimBehind, true
328         ));
329     }
330 
331     @Test
transitionToShadeLocked()332     public void transitionToShadeLocked() {
333         mScrimController.transitionTo(SHADE_LOCKED);
334         mScrimController.setQsPosition(1f, 0);
335         finishAnimationsImmediately();
336 
337         assertScrimAlpha(Map.of(
338                 mNotificationsScrim, OPAQUE,
339                 mScrimInFront, TRANSPARENT,
340                 mScrimBehind, OPAQUE));
341 
342         assertScrimTinted(Map.of(
343                 mScrimInFront, false,
344                 mScrimBehind, true
345         ));
346     }
347 
348     @Test
transitionToShadeLocked_clippingQs()349     public void transitionToShadeLocked_clippingQs() {
350         mScrimController.setClipsQsScrim(true);
351         mScrimController.transitionTo(SHADE_LOCKED);
352         mScrimController.setQsPosition(1f, 0);
353         finishAnimationsImmediately();
354 
355         assertScrimAlpha(Map.of(
356                 mNotificationsScrim, OPAQUE,
357                 mScrimInFront, TRANSPARENT,
358                 mScrimBehind, OPAQUE));
359 
360         assertScrimTinted(Map.of(
361                 mScrimInFront, false,
362                 mScrimBehind, true
363         ));
364     }
365 
366     @Test
transitionToOff()367     public void transitionToOff() {
368         mScrimController.transitionTo(ScrimState.OFF);
369         finishAnimationsImmediately();
370 
371         assertScrimAlpha(Map.of(
372                 mScrimInFront, OPAQUE,
373                 mScrimBehind, OPAQUE));
374 
375         assertScrimTinted(Map.of(
376                 mScrimInFront, true,
377                 mScrimBehind, true
378         ));
379 
380         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
381     }
382 
383     @Test
transitionToAod_withRegularWallpaper()384     public void transitionToAod_withRegularWallpaper() {
385         mScrimController.transitionTo(ScrimState.AOD);
386         finishAnimationsImmediately();
387 
388         assertScrimAlpha(Map.of(
389                 mScrimInFront, TRANSPARENT,
390                 mScrimBehind, TRANSPARENT,
391                 mNotificationsScrim, TRANSPARENT));
392         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
393 
394         assertScrimTinted(Map.of(
395                 mScrimInFront, true,
396                 mScrimBehind, true
397         ));
398     }
399 
400     @Test
transitionToAod_withAodWallpaper()401     public void transitionToAod_withAodWallpaper() {
402         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
403         mTestScope.getTestScheduler().runCurrent();
404 
405         mScrimController.transitionTo(ScrimState.AOD);
406         finishAnimationsImmediately();
407 
408         assertScrimAlpha(Map.of(
409                 mScrimInFront, TRANSPARENT,
410                 mScrimBehind, TRANSPARENT));
411         assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
412 
413         // Pulsing notification should conserve AOD wallpaper.
414         mScrimController.transitionTo(ScrimState.PULSING);
415         finishAnimationsImmediately();
416 
417         assertScrimAlpha(Map.of(
418                 mScrimInFront, TRANSPARENT,
419                 mScrimBehind, TRANSPARENT));
420         assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
421     }
422 
423     @Test
transitionToAod_withAodWallpaperAndLockScreenWallpaper()424     public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
425         mScrimController.setHasBackdrop(true);
426         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
427         mTestScope.getTestScheduler().runCurrent();
428 
429         mScrimController.transitionTo(ScrimState.AOD);
430         finishAnimationsImmediately();
431 
432         assertScrimAlpha(Map.of(
433                 mScrimInFront, TRANSPARENT,
434                 mScrimBehind, TRANSPARENT));
435         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
436 
437         assertScrimTinted(Map.of(
438                 mScrimInFront, true,
439                 mScrimBehind, true
440         ));
441     }
442 
443     @Test
setHasBackdrop_withAodWallpaperAndAlbumArt()444     public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
445         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
446         mTestScope.getTestScheduler().runCurrent();
447 
448         mScrimController.transitionTo(ScrimState.AOD);
449         finishAnimationsImmediately();
450         mScrimController.setHasBackdrop(true);
451         finishAnimationsImmediately();
452 
453         assertScrimAlpha(Map.of(
454                 mScrimInFront, TRANSPARENT,
455                 mScrimBehind, TRANSPARENT));
456         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
457 
458         assertScrimTinted(Map.of(
459                 mScrimInFront, true,
460                 mScrimBehind, true
461         ));
462     }
463 
464     @Test
transitionToAod_withFrontAlphaUpdates()465     public void transitionToAod_withFrontAlphaUpdates() {
466         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
467         mScrimController.transitionTo(ScrimState.KEYGUARD);
468         mScrimController.setAodFrontScrimAlpha(0.5f);
469         finishAnimationsImmediately();
470 
471         assertScrimAlpha(Map.of(
472                 mScrimInFront, TRANSPARENT,
473                 mScrimBehind, SEMI_TRANSPARENT));
474 
475         // ... but that it does take effect once we enter the AOD state.
476         mScrimController.transitionTo(ScrimState.AOD);
477         finishAnimationsImmediately();
478         assertScrimAlpha(Map.of(
479                 mScrimInFront, SEMI_TRANSPARENT,
480                 mScrimBehind, TRANSPARENT));
481         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
482 
483         // ... and that if we set it while we're in AOD, it does take immediate effect.
484         mScrimController.setAodFrontScrimAlpha(1f);
485         assertScrimAlpha(Map.of(
486                 mScrimInFront, OPAQUE,
487                 mScrimBehind, TRANSPARENT));
488         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
489 
490         // ... and make sure we recall the previous front scrim alpha even if we transition away
491         // for a bit.
492         mScrimController.transitionTo(ScrimState.UNLOCKED);
493         mScrimController.transitionTo(ScrimState.AOD);
494         finishAnimationsImmediately();
495         assertScrimAlpha(Map.of(
496                 mScrimInFront, OPAQUE,
497                 mScrimBehind, TRANSPARENT));
498         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
499 
500         // ... and alpha updates should be completely ignored if always_on is off.
501         // Passing it forward would mess up the wake-up transition.
502         mAlwaysOnEnabled = false;
503         mScrimController.transitionTo(ScrimState.UNLOCKED);
504         mScrimController.transitionTo(ScrimState.AOD);
505         finishAnimationsImmediately();
506         mScrimController.setAodFrontScrimAlpha(0.3f);
507         assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f);
508         Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f);
509     }
510 
511     @Test
transitionToAod_afterDocked_ignoresAlwaysOnAndUpdatesFrontAlpha()512     public void transitionToAod_afterDocked_ignoresAlwaysOnAndUpdatesFrontAlpha() {
513         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
514         mScrimController.transitionTo(ScrimState.KEYGUARD);
515         mScrimController.setAodFrontScrimAlpha(0.5f);
516         finishAnimationsImmediately();
517 
518         assertScrimAlpha(Map.of(
519                 mScrimInFront, TRANSPARENT,
520                 mScrimBehind, SEMI_TRANSPARENT));
521 
522         // ... and doesn't take effect when disabled always_on
523         mAlwaysOnEnabled = false;
524         mScrimController.transitionTo(ScrimState.AOD);
525         finishAnimationsImmediately();
526         assertScrimAlpha(Map.of(
527                 mScrimInFront, OPAQUE,
528                 mScrimBehind, TRANSPARENT));
529         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
530 
531         // ... but will take effect after docked
532         when(mDockManager.isDocked()).thenReturn(true);
533         mScrimController.transitionTo(ScrimState.KEYGUARD);
534         mScrimController.setAodFrontScrimAlpha(0.5f);
535         mScrimController.transitionTo(ScrimState.AOD);
536         finishAnimationsImmediately();
537 
538         assertScrimAlpha(Map.of(
539                 mScrimInFront, SEMI_TRANSPARENT,
540                 mScrimBehind, TRANSPARENT));
541         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
542 
543         // ... and that if we set it while we're in AOD, it does take immediate effect after docked.
544         mScrimController.setAodFrontScrimAlpha(1f);
545         finishAnimationsImmediately();
546         assertScrimAlpha(Map.of(
547                 mScrimInFront, OPAQUE,
548                 mScrimBehind, TRANSPARENT));
549         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
550 
551         // Reset value since enums are static.
552         mScrimController.setAodFrontScrimAlpha(0f);
553     }
554 
555     @Test
transitionToPulsing_withFrontAlphaUpdates()556     public void transitionToPulsing_withFrontAlphaUpdates() {
557         // Pre-condition
558         // Need to go to AoD first because PULSING doesn't change
559         // the back scrim opacity - otherwise it would hide AoD wallpapers.
560         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
561         mTestScope.getTestScheduler().runCurrent();
562 
563         mScrimController.transitionTo(ScrimState.AOD);
564         finishAnimationsImmediately();
565         assertScrimAlpha(Map.of(
566                 mScrimInFront, TRANSPARENT,
567                 mScrimBehind, TRANSPARENT));
568         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
569 
570         mScrimController.transitionTo(ScrimState.PULSING);
571         finishAnimationsImmediately();
572         // Front scrim should be transparent, but tinted
573         // Back scrim should be semi-transparent so the user can see the wallpaper
574         // Pulse callback should have been invoked
575         assertScrimAlpha(Map.of(
576                 mScrimInFront, TRANSPARENT,
577                 mScrimBehind, TRANSPARENT));
578         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
579 
580         assertScrimTinted(Map.of(
581                 mScrimInFront, true,
582                 mScrimBehind, true
583         ));
584 
585         // ... and when ambient goes dark, front scrim should be semi-transparent
586         mScrimController.setAodFrontScrimAlpha(0.5f);
587         finishAnimationsImmediately();
588         // Front scrim should be semi-transparent
589         assertScrimAlpha(Map.of(
590                 mScrimInFront, SEMI_TRANSPARENT,
591                 mScrimBehind, TRANSPARENT));
592         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
593 
594         mScrimController.setWakeLockScreenSensorActive(true);
595         finishAnimationsImmediately();
596         assertScrimAlpha(Map.of(
597                 mScrimInFront, SEMI_TRANSPARENT,
598                 mScrimBehind, TRANSPARENT));
599         assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA,
600                 mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
601 
602         // Reset value since enums are static.
603         mScrimController.setAodFrontScrimAlpha(0f);
604     }
605 
606     @Test
transitionToKeyguardBouncer()607     public void transitionToKeyguardBouncer() {
608         mScrimController.transitionTo(BOUNCER);
609         finishAnimationsImmediately();
610         // Front scrim should be transparent
611         // Back scrim should be visible and tinted to the surface color
612         assertScrimAlpha(Map.of(
613                 mScrimInFront, TRANSPARENT,
614                 mNotificationsScrim, TRANSPARENT,
615                 mScrimBehind, OPAQUE));
616 
617         assertScrimTinted(Map.of(
618                 mScrimInFront, false,
619                 mScrimBehind, true,
620                 mNotificationsScrim, false
621         ));
622 
623         assertScrimTint(mScrimBehind, mSurfaceColor);
624     }
625 
626     @Test
onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor()627     public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() {
628         assertEquals(BOUNCER.getBehindTint(), 0x112233);
629         mSurfaceColor = 0x223344;
630         mConfigurationController.notifyThemeChanged();
631         assertEquals(BOUNCER.getBehindTint(), 0x223344);
632     }
633 
634     @Test
onThemeChangeWhileClipQsScrim_bouncerBehindTint_remainsBlack()635     public void onThemeChangeWhileClipQsScrim_bouncerBehindTint_remainsBlack() {
636         mScrimController.setClipsQsScrim(true);
637         mScrimController.transitionTo(BOUNCER);
638         finishAnimationsImmediately();
639 
640         assertEquals(BOUNCER.getBehindTint(), Color.BLACK);
641         mSurfaceColor = 0x223344;
642         mConfigurationController.notifyThemeChanged();
643         assertEquals(BOUNCER.getBehindTint(), Color.BLACK);
644     }
645 
646     @Test
transitionToKeyguardBouncer_clippingQs()647     public void transitionToKeyguardBouncer_clippingQs() {
648         mScrimController.setClipsQsScrim(true);
649         mScrimController.transitionTo(BOUNCER);
650         finishAnimationsImmediately();
651         // Front scrim should be transparent
652         // Back scrim should be clipping QS
653         // Notif scrim should be visible without tint
654         assertScrimAlpha(Map.of(
655                 mScrimInFront, TRANSPARENT,
656                 mNotificationsScrim, OPAQUE,
657                 mScrimBehind, OPAQUE));
658 
659         assertScrimTinted(Map.of(
660                 mScrimInFront, false,
661                 mScrimBehind, true,
662                 mNotificationsScrim, false
663         ));
664     }
665 
666     @Test
disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha()667     public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
668         mScrimController.setClipsQsScrim(true);
669         mScrimController.transitionTo(BOUNCER);
670 
671         mScrimController.setClipsQsScrim(false);
672 
673         finishAnimationsImmediately();
674         // Front scrim should be transparent
675         // Back scrim should be visible and has a tint of surfaceColor
676         assertScrimAlpha(Map.of(
677                 mScrimInFront, TRANSPARENT,
678                 mNotificationsScrim, TRANSPARENT,
679                 mScrimBehind, OPAQUE));
680         assertScrimTinted(Map.of(
681                 mScrimInFront, false,
682                 mScrimBehind, true,
683                 mNotificationsScrim, false
684         ));
685         assertScrimTint(mScrimBehind, mSurfaceColor);
686     }
687 
688     @Test
enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha()689     public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
690         mScrimController.setClipsQsScrim(false);
691         mScrimController.transitionTo(BOUNCER);
692 
693         mScrimController.setClipsQsScrim(true);
694 
695         finishAnimationsImmediately();
696         // Front scrim should be transparent
697         // Back scrim should be clipping QS
698         // Notif scrim should be visible without tint
699         assertScrimAlpha(Map.of(
700                 mScrimInFront, TRANSPARENT,
701                 mNotificationsScrim, OPAQUE,
702                 mScrimBehind, OPAQUE));
703         assertScrimTinted(Map.of(
704                 mScrimInFront, false,
705                 mScrimBehind, true,
706                 mNotificationsScrim, false
707         ));
708     }
709 
710     @Test
transitionToBouncer()711     public void transitionToBouncer() {
712         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
713         finishAnimationsImmediately();
714         assertScrimAlpha(Map.of(
715                 mScrimInFront, OPAQUE,
716                 mScrimBehind, TRANSPARENT));
717         assertScrimTinted(Map.of(
718                 mScrimInFront, false,
719                 mScrimBehind, false
720         ));
721     }
722 
723     @Test
transitionToUnlocked_clippedQs()724     public void transitionToUnlocked_clippedQs() {
725         mScrimController.setClipsQsScrim(true);
726         mScrimController.setRawPanelExpansionFraction(0f);
727         mScrimController.transitionTo(ScrimState.UNLOCKED);
728         finishAnimationsImmediately();
729 
730         assertScrimTinted(Map.of(
731                 mNotificationsScrim, false,
732                 mScrimInFront, false,
733                 mScrimBehind, true
734         ));
735         assertScrimAlpha(Map.of(
736                 mScrimInFront, TRANSPARENT,
737                 mNotificationsScrim, TRANSPARENT,
738                 mScrimBehind, OPAQUE));
739 
740         mScrimController.setRawPanelExpansionFraction(0.25f);
741         assertScrimAlpha(Map.of(
742                 mScrimInFront, TRANSPARENT,
743                 mNotificationsScrim, SEMI_TRANSPARENT,
744                 mScrimBehind, OPAQUE));
745 
746         mScrimController.setRawPanelExpansionFraction(0.5f);
747         assertScrimAlpha(Map.of(
748                 mScrimInFront, TRANSPARENT,
749                 mNotificationsScrim, OPAQUE,
750                 mScrimBehind, OPAQUE));
751     }
752 
753     @Test
transitionToUnlocked_nonClippedQs_followsLargeScreensInterpolator()754     public void transitionToUnlocked_nonClippedQs_followsLargeScreensInterpolator() {
755         mScrimController.setClipsQsScrim(false);
756         mScrimController.setRawPanelExpansionFraction(0f);
757         mScrimController.transitionTo(ScrimState.UNLOCKED);
758         finishAnimationsImmediately();
759 
760         assertScrimTinted(Map.of(
761                 mNotificationsScrim, false,
762                 mScrimInFront, false,
763                 mScrimBehind, true
764         ));
765         // The large screens interpolator used in this test is a linear one, just for tests.
766         // Assertions below are based on this assumption, and that the code uses that interpolator
767         // when on a large screen (QS not clipped).
768         assertScrimAlpha(Map.of(
769                 mScrimInFront, TRANSPARENT,
770                 mNotificationsScrim, TRANSPARENT,
771                 mScrimBehind, TRANSPARENT));
772 
773         mScrimController.setRawPanelExpansionFraction(0.5f);
774         assertScrimAlpha(Map.of(
775                 mScrimInFront, TRANSPARENT,
776                 mNotificationsScrim, SEMI_TRANSPARENT,
777                 mScrimBehind, SEMI_TRANSPARENT));
778 
779         mScrimController.setRawPanelExpansionFraction(0.99f);
780         assertScrimAlpha(Map.of(
781                 mScrimInFront, TRANSPARENT,
782                 mNotificationsScrim, SEMI_TRANSPARENT,
783                 mScrimBehind, SEMI_TRANSPARENT));
784 
785         mScrimController.setRawPanelExpansionFraction(1f);
786         assertScrimAlpha(Map.of(
787                 mScrimInFront, TRANSPARENT,
788                 mNotificationsScrim, OPAQUE,
789                 mScrimBehind, OPAQUE));
790     }
791 
792     @Test
scrimStateCallback()793     public void scrimStateCallback() {
794         mScrimController.transitionTo(ScrimState.UNLOCKED);
795         finishAnimationsImmediately();
796         assertEquals(mScrimState, ScrimState.UNLOCKED);
797 
798         mScrimController.transitionTo(BOUNCER);
799         finishAnimationsImmediately();
800         assertEquals(mScrimState, BOUNCER);
801 
802         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
803         finishAnimationsImmediately();
804         assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
805     }
806 
807     @Test
panelExpansion()808     public void panelExpansion() {
809         mScrimController.setRawPanelExpansionFraction(0f);
810         mScrimController.setRawPanelExpansionFraction(0.5f);
811         mScrimController.transitionTo(ScrimState.UNLOCKED);
812         finishAnimationsImmediately();
813 
814         reset(mScrimBehind);
815         mScrimController.setRawPanelExpansionFraction(0f);
816         mScrimController.setRawPanelExpansionFraction(1.0f);
817         finishAnimationsImmediately();
818 
819         assertEquals("Scrim alpha should change after setPanelExpansion",
820                 mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
821 
822         mScrimController.setRawPanelExpansionFraction(0f);
823         finishAnimationsImmediately();
824 
825         assertEquals("Scrim alpha should change after setPanelExpansion",
826                 mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
827     }
828 
829     @Test
qsExpansion()830     public void qsExpansion() {
831         reset(mScrimBehind);
832         mScrimController.setQsPosition(1f, 999 /* value doesn't matter */);
833         finishAnimationsImmediately();
834 
835         assertScrimAlpha(Map.of(
836                 mScrimInFront, TRANSPARENT,
837                 mScrimBehind, OPAQUE,
838                 mNotificationsScrim, OPAQUE));
839     }
840 
841     @Test
qsExpansion_clippingQs()842     public void qsExpansion_clippingQs() {
843         reset(mScrimBehind);
844         mScrimController.setClipsQsScrim(true);
845         mScrimController.setQsPosition(1f, 999 /* value doesn't matter */);
846         finishAnimationsImmediately();
847 
848         assertScrimAlpha(Map.of(
849                 mScrimInFront, TRANSPARENT,
850                 mScrimBehind, OPAQUE,
851                 mNotificationsScrim, OPAQUE));
852     }
853 
854     @Test
qsExpansion_half_clippingQs()855     public void qsExpansion_half_clippingQs() {
856         reset(mScrimBehind);
857         mScrimController.setClipsQsScrim(true);
858         mScrimController.setQsPosition(0.25f, 999 /* value doesn't matter */);
859         finishAnimationsImmediately();
860 
861         assertScrimAlpha(Map.of(
862                 mScrimInFront, TRANSPARENT,
863                 mScrimBehind, OPAQUE,
864                 mNotificationsScrim, SEMI_TRANSPARENT));
865     }
866 
867     @Test
panelExpansionAffectsAlpha()868     public void panelExpansionAffectsAlpha() {
869         mScrimController.setRawPanelExpansionFraction(0f);
870         mScrimController.setRawPanelExpansionFraction(0.5f);
871         mScrimController.transitionTo(ScrimState.UNLOCKED);
872         finishAnimationsImmediately();
873 
874         final float scrimAlpha = mScrimBehind.getViewAlpha();
875         reset(mScrimBehind);
876         mScrimController.setExpansionAffectsAlpha(false);
877         mScrimController.setRawPanelExpansionFraction(0.8f);
878         verifyZeroInteractions(mScrimBehind);
879         assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
880                 + "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
881 
882         mScrimController.setExpansionAffectsAlpha(true);
883         mScrimController.setRawPanelExpansionFraction(0.1f);
884         finishAnimationsImmediately();
885         Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
886                 + "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
887     }
888 
889     @Test
transitionToUnlockedFromOff()890     public void transitionToUnlockedFromOff() {
891         // Simulate unlock with fingerprint without AOD
892         mScrimController.transitionTo(ScrimState.OFF);
893         mScrimController.setRawPanelExpansionFraction(0f);
894         finishAnimationsImmediately();
895         mScrimController.transitionTo(ScrimState.UNLOCKED);
896 
897         finishAnimationsImmediately();
898 
899         // All scrims should be transparent at the end of fade transition.
900         assertScrimAlpha(Map.of(
901                 mScrimInFront, TRANSPARENT,
902                 mScrimBehind, TRANSPARENT));
903 
904         // Make sure at the very end of the animation, we're reset to transparent
905         assertScrimTinted(Map.of(
906                 mScrimInFront, false,
907                 mScrimBehind, true
908         ));
909     }
910 
911     @Test
transitionToUnlockedFromAod()912     public void transitionToUnlockedFromAod() {
913         // Simulate unlock with fingerprint
914         mScrimController.transitionTo(ScrimState.AOD);
915         mScrimController.setRawPanelExpansionFraction(0f);
916         finishAnimationsImmediately();
917         mScrimController.transitionTo(ScrimState.UNLOCKED);
918 
919         finishAnimationsImmediately();
920 
921         // All scrims should be transparent at the end of fade transition.
922         assertScrimAlpha(Map.of(
923                 mScrimInFront, TRANSPARENT,
924                 mScrimBehind, TRANSPARENT));
925 
926         // Make sure at the very end of the animation, we're reset to transparent
927         assertScrimTinted(Map.of(
928                 mScrimInFront, false,
929                 mScrimBehind, true
930         ));
931     }
932 
933     @Test
scrimBlanksBeforeLeavingAod()934     public void scrimBlanksBeforeLeavingAod() {
935         // Simulate unlock with fingerprint
936         mScrimController.transitionTo(ScrimState.AOD);
937         finishAnimationsImmediately();
938         mScrimController.transitionTo(ScrimState.UNLOCKED,
939                 new ScrimController.Callback() {
940                     @Override
941                     public void onDisplayBlanked() {
942                         // Front scrim should be black in the middle of the transition
943                         Assert.assertTrue("Scrim should be visible during transition. Alpha: "
944                                 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
945                         assertScrimTinted(Map.of(
946                                 mScrimInFront, true,
947                                 mScrimBehind, true
948                         ));
949                         Assert.assertSame("Scrim should be visible during transition.",
950                                 mScrimVisibility, OPAQUE);
951                     }
952                 });
953         finishAnimationsImmediately();
954     }
955 
956     @Test
scrimBlankCallbackWhenUnlockingFromPulse()957     public void scrimBlankCallbackWhenUnlockingFromPulse() {
958         boolean[] blanked = {false};
959         // Simulate unlock with fingerprint
960         mScrimController.transitionTo(ScrimState.PULSING);
961         finishAnimationsImmediately();
962         mScrimController.transitionTo(ScrimState.UNLOCKED,
963                 new ScrimController.Callback() {
964                     @Override
965                     public void onDisplayBlanked() {
966                         blanked[0] = true;
967                     }
968                 });
969         finishAnimationsImmediately();
970         Assert.assertTrue("Scrim should send display blanked callback when unlocking "
971                 + "from pulse.", blanked[0]);
972     }
973 
974     @Test
blankingNotRequired_leavingAoD()975     public void blankingNotRequired_leavingAoD() {
976         // GIVEN display does NOT need blanking
977         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
978 
979         mScrimController = new ScrimController(
980                 mLightBarController,
981                 mDozeParameters,
982                 mAlarmManager,
983                 mKeyguardStateController,
984                 mDelayedWakeLockBuilder,
985                 new FakeHandler(mLooper.getLooper()),
986                 mKeyguardUpdateMonitor,
987                 mDockManager,
988                 mConfigurationController,
989                 new FakeExecutor(new FakeSystemClock()),
990                 mJavaAdapter,
991                 mScreenOffAnimationController,
992                 mKeyguardUnlockAnimationController,
993                 mStatusBarKeyguardViewManager,
994                 mPrimaryBouncerToGoneTransitionViewModel,
995                 mKeyguardTransitionInteractor,
996                 mWallpaperRepository,
997                 mMainDispatcher,
998                 mLinearLargeScreenShadeInterpolator);
999         mScrimController.start();
1000         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
1001         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
1002         mScrimController.setAnimatorListener(mAnimatorListener);
1003         mScrimController.setHasBackdrop(false);
1004         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
1005         mTestScope.getTestScheduler().runCurrent();
1006         mScrimController.transitionTo(ScrimState.KEYGUARD);
1007         finishAnimationsImmediately();
1008 
1009         // WHEN Simulate unlock with fingerprint
1010         mScrimController.transitionTo(ScrimState.AOD);
1011         finishAnimationsImmediately();
1012 
1013         // WHEN transitioning to UNLOCKED, onDisplayCallbackBlanked callback called to continue
1014         // the transition but the scrim was not actually blanked
1015         mScrimController.transitionTo(ScrimState.UNLOCKED,
1016                 new ScrimController.Callback() {
1017                     @Override
1018                     public void onDisplayBlanked() {
1019                         // Front scrim should not be black nor opaque
1020                         Assert.assertTrue("Scrim should NOT be visible during transition."
1021                                 + " Alpha: " + mScrimInFront.getViewAlpha(),
1022                                 mScrimInFront.getViewAlpha() == 0f);
1023                         Assert.assertSame("Scrim should not be visible during transition.",
1024                                 mScrimVisibility, TRANSPARENT);
1025                     }
1026                 });
1027         finishAnimationsImmediately();
1028     }
1029 
1030     @Test
testScrimCallback()1031     public void testScrimCallback() {
1032         int[] callOrder = {0, 0, 0};
1033         int[] currentCall = {0};
1034         mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
1035             @Override
1036             public void onStart() {
1037                 callOrder[0] = ++currentCall[0];
1038             }
1039 
1040             @Override
1041             public void onDisplayBlanked() {
1042                 callOrder[1] = ++currentCall[0];
1043             }
1044 
1045             @Override
1046             public void onFinished() {
1047                 callOrder[2] = ++currentCall[0];
1048             }
1049         });
1050         finishAnimationsImmediately();
1051         assertEquals("onStart called in wrong order", 1, callOrder[0]);
1052         assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
1053         assertEquals("onFinished called in wrong order", 3, callOrder[2]);
1054     }
1055 
1056     @Test
testScrimCallbacksWithoutAmbientDisplay()1057     public void testScrimCallbacksWithoutAmbientDisplay() {
1058         mAlwaysOnEnabled = false;
1059         testScrimCallback();
1060     }
1061 
1062     @Test
testScrimCallbackCancelled()1063     public void testScrimCallbackCancelled() {
1064         boolean[] cancelledCalled = {false};
1065         mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
1066             @Override
1067             public void onCancelled() {
1068                 cancelledCalled[0] = true;
1069             }
1070         });
1071         mScrimController.transitionTo(ScrimState.PULSING);
1072         Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
1073     }
1074 
1075     @Test
testHoldsWakeLock_whenAOD()1076     public void testHoldsWakeLock_whenAOD() {
1077         mScrimController.transitionTo(ScrimState.AOD);
1078         verify(mWakeLock).acquire(anyString());
1079         verify(mWakeLock, never()).release(anyString());
1080         finishAnimationsImmediately();
1081         verify(mWakeLock).release(anyString());
1082     }
1083 
1084     @Test
testDoesNotHoldWakeLock_whenUnlocking()1085     public void testDoesNotHoldWakeLock_whenUnlocking() {
1086         mScrimController.transitionTo(ScrimState.UNLOCKED);
1087         finishAnimationsImmediately();
1088         verifyZeroInteractions(mWakeLock);
1089     }
1090 
1091     @Test
testCallbackInvokedOnSameStateTransition()1092     public void testCallbackInvokedOnSameStateTransition() {
1093         mScrimController.transitionTo(ScrimState.UNLOCKED);
1094         finishAnimationsImmediately();
1095         ScrimController.Callback callback = mock(ScrimController.Callback.class);
1096         mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
1097         verify(callback).onFinished();
1098     }
1099 
1100     @Test
testHoldsAodWallpaperAnimationLock()1101     public void testHoldsAodWallpaperAnimationLock() {
1102         // Pre-conditions
1103         mScrimController.transitionTo(ScrimState.AOD);
1104         finishAnimationsImmediately();
1105         reset(mWakeLock);
1106 
1107         mScrimController.onHideWallpaperTimeout();
1108         verify(mWakeLock).acquire(anyString());
1109         verify(mWakeLock, never()).release(anyString());
1110         finishAnimationsImmediately();
1111         verify(mWakeLock).release(anyString());
1112     }
1113 
1114     @Test
testHoldsPulsingWallpaperAnimationLock()1115     public void testHoldsPulsingWallpaperAnimationLock() {
1116         // Pre-conditions
1117         mScrimController.transitionTo(ScrimState.PULSING);
1118         finishAnimationsImmediately();
1119         reset(mWakeLock);
1120 
1121         mScrimController.onHideWallpaperTimeout();
1122         verify(mWakeLock).acquire(anyString());
1123         verify(mWakeLock, never()).release(anyString());
1124         finishAnimationsImmediately();
1125         verify(mWakeLock).release(anyString());
1126     }
1127 
1128     @Test
testWillHideAodWallpaper()1129     public void testWillHideAodWallpaper() {
1130         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
1131         mTestScope.getTestScheduler().runCurrent();
1132 
1133         mScrimController.transitionTo(ScrimState.AOD);
1134         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
1135         mScrimController.transitionTo(ScrimState.KEYGUARD);
1136         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
1137     }
1138 
1139     @Test
testWillHideDockedWallpaper()1140     public void testWillHideDockedWallpaper() {
1141         mAlwaysOnEnabled = false;
1142         when(mDockManager.isDocked()).thenReturn(true);
1143         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
1144         mTestScope.getTestScheduler().runCurrent();
1145 
1146         mScrimController.transitionTo(ScrimState.AOD);
1147 
1148         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
1149     }
1150 
1151     @Test
testConservesExpansionOpacityAfterTransition()1152     public void testConservesExpansionOpacityAfterTransition() {
1153         mScrimController.transitionTo(ScrimState.UNLOCKED);
1154         mScrimController.setRawPanelExpansionFraction(0.5f);
1155         finishAnimationsImmediately();
1156 
1157         final float expandedAlpha = mScrimBehind.getViewAlpha();
1158 
1159         mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
1160         finishAnimationsImmediately();
1161         mScrimController.transitionTo(ScrimState.UNLOCKED);
1162         finishAnimationsImmediately();
1163 
1164         assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
1165                 expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
1166     }
1167 
1168     @Test
testCancelsOldAnimationBeforeBlanking()1169     public void testCancelsOldAnimationBeforeBlanking() {
1170         mScrimController.transitionTo(ScrimState.AOD);
1171         finishAnimationsImmediately();
1172         // Consume whatever value we had before
1173         mAnimatorListener.reset();
1174 
1175         mScrimController.transitionTo(ScrimState.KEYGUARD);
1176         finishAnimationsImmediately();
1177         Assert.assertTrue("Animators not canceled", mAnimatorListener.getNumCancels() != 0);
1178     }
1179 
1180     @Test
testScrimFocus()1181     public void testScrimFocus() {
1182         mScrimController.transitionTo(ScrimState.AOD);
1183         assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
1184         assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable());
1185 
1186         mScrimController.transitionTo(ScrimState.KEYGUARD);
1187         Assert.assertTrue("Should be focusable on keyguard", mScrimBehind.isFocusable());
1188         Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
1189     }
1190 
1191     @Test
testHidesShowWhenLockedActivity()1192     public void testHidesShowWhenLockedActivity() {
1193         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
1194         mTestScope.getTestScheduler().runCurrent();
1195 
1196         mScrimController.setKeyguardOccluded(true);
1197         mScrimController.transitionTo(ScrimState.AOD);
1198         finishAnimationsImmediately();
1199         assertScrimAlpha(Map.of(
1200                 mScrimInFront, TRANSPARENT,
1201                 mScrimBehind, OPAQUE));
1202 
1203         mScrimController.transitionTo(ScrimState.PULSING);
1204         finishAnimationsImmediately();
1205         assertScrimAlpha(Map.of(
1206                 mScrimInFront, TRANSPARENT,
1207                 mScrimBehind, OPAQUE));
1208     }
1209 
1210     @Test
testHidesShowWhenLockedActivity_whenAlreadyInAod()1211     public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
1212         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
1213         mTestScope.getTestScheduler().runCurrent();
1214 
1215         mScrimController.transitionTo(ScrimState.AOD);
1216         finishAnimationsImmediately();
1217         assertScrimAlpha(Map.of(
1218                 mScrimInFront, TRANSPARENT,
1219                 mScrimBehind, TRANSPARENT));
1220 
1221         mScrimController.setKeyguardOccluded(true);
1222         finishAnimationsImmediately();
1223         assertScrimAlpha(Map.of(
1224                 mScrimInFront, TRANSPARENT,
1225                 mScrimBehind, OPAQUE));
1226     }
1227 
1228     @Test
testEatsTouchEvent()1229     public void testEatsTouchEvent() {
1230         HashSet<ScrimState> eatsTouches =
1231                 new HashSet<>(Collections.singletonList(ScrimState.AOD));
1232         for (ScrimState state : ScrimState.values()) {
1233             if (state == ScrimState.UNINITIALIZED) {
1234                 continue;
1235             }
1236             mScrimController.transitionTo(state);
1237             finishAnimationsImmediately();
1238             assertEquals("Should be clickable unless AOD or PULSING, was: " + state,
1239                     mScrimBehind.getViewAlpha() != 0 && !eatsTouches.contains(state),
1240                     mScrimBehind.isClickable());
1241         }
1242     }
1243 
1244     @Test
testAnimatesTransitionToAod()1245     public void testAnimatesTransitionToAod() {
1246         when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
1247         ScrimState.AOD.prepare(ScrimState.KEYGUARD);
1248         assertFalse("No animation when ColorFade kicks in",
1249                 ScrimState.AOD.getAnimateChange());
1250 
1251         reset(mDozeParameters);
1252         when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
1253         ScrimState.AOD.prepare(ScrimState.KEYGUARD);
1254         Assert.assertTrue("Animate scrims when ColorFade won't be triggered",
1255                 ScrimState.AOD.getAnimateChange());
1256     }
1257 
1258     @Test
testViewsDontHaveFocusHighlight()1259     public void testViewsDontHaveFocusHighlight() {
1260         assertFalse("Scrim shouldn't have focus highlight",
1261                 mScrimInFront.getDefaultFocusHighlightEnabled());
1262         assertFalse("Scrim shouldn't have focus highlight",
1263                 mScrimBehind.getDefaultFocusHighlightEnabled());
1264     }
1265 
1266     @Test
testIsLowPowerMode()1267     public void testIsLowPowerMode() {
1268         HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
1269                 ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
1270         HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
1271                 ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER,
1272                 ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR,
1273                 ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
1274                 ScrimState.AUTH_SCRIMMED_SHADE));
1275 
1276         for (ScrimState state : ScrimState.values()) {
1277             if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
1278                 Assert.fail("Scrim state isn't categorized as a low power or regular state.");
1279             }
1280         }
1281     }
1282 
1283     @Test
testScrimsOpaque_whenShadeFullyExpanded()1284     public void testScrimsOpaque_whenShadeFullyExpanded() {
1285         mScrimController.transitionTo(ScrimState.UNLOCKED);
1286         mScrimController.setRawPanelExpansionFraction(1);
1287         // notifications scrim alpha change require calling setQsPosition
1288         mScrimController.setQsPosition(0, 300);
1289         finishAnimationsImmediately();
1290 
1291         assertEquals("Behind scrim should be opaque",
1292                 mScrimBehind.getViewAlpha(), 1, 0.0);
1293         assertEquals("Notifications scrim should be opaque",
1294                 mNotificationsScrim.getViewAlpha(), 1, 0.0);
1295     }
1296 
1297     @Test
testAuthScrim_setClipQSScrimTrue_notifScrimOpaque_whenShadeFullyExpanded()1298     public void testAuthScrim_setClipQSScrimTrue_notifScrimOpaque_whenShadeFullyExpanded() {
1299         // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
1300         // with the camera app occluding the keyguard)
1301         mScrimController.transitionTo(ScrimState.UNLOCKED);
1302         mScrimController.setClipsQsScrim(true);
1303         mScrimController.setRawPanelExpansionFraction(1);
1304         // notifications scrim alpha change require calling setQsPosition
1305         mScrimController.setQsPosition(0, 300);
1306         finishAnimationsImmediately();
1307 
1308         // WHEN the user triggers the auth bouncer
1309         mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
1310         finishAnimationsImmediately();
1311 
1312         assertEquals("Behind scrim should be opaque",
1313                 mScrimBehind.getViewAlpha(), 1, 0.0);
1314         assertEquals("Notifications scrim should be opaque",
1315                 mNotificationsScrim.getViewAlpha(), 1, 0.0);
1316 
1317         assertScrimTinted(Map.of(
1318                 mScrimInFront, true,
1319                 mScrimBehind, true,
1320                 mNotificationsScrim, false
1321         ));
1322     }
1323 
1324 
1325     @Test
testAuthScrim_setClipQSScrimFalse_notifScrimOpaque_whenShadeFullyExpanded()1326     public void testAuthScrim_setClipQSScrimFalse_notifScrimOpaque_whenShadeFullyExpanded() {
1327         // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
1328         // with the camera app occluding the keyguard)
1329         mScrimController.transitionTo(ScrimState.UNLOCKED);
1330         mScrimController.setClipsQsScrim(false);
1331         mScrimController.setRawPanelExpansionFraction(1);
1332         // notifications scrim alpha change require calling setQsPosition
1333         mScrimController.setQsPosition(0, 300);
1334         finishAnimationsImmediately();
1335 
1336         // WHEN the user triggers the auth bouncer
1337         mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
1338         finishAnimationsImmediately();
1339 
1340         assertEquals("Behind scrim should be opaque",
1341                 mScrimBehind.getViewAlpha(), 1, 0.0);
1342         assertEquals("Notifications scrim should be opaque",
1343                 mNotificationsScrim.getViewAlpha(), 1, 0.0);
1344 
1345         assertScrimTinted(Map.of(
1346                 mScrimInFront, true,
1347                 mScrimBehind, true,
1348                 mNotificationsScrim, false
1349         ));
1350     }
1351 
1352     @Test
testAuthScrimKeyguard()1353     public void testAuthScrimKeyguard() {
1354         // GIVEN device is on the keyguard
1355         mScrimController.transitionTo(ScrimState.KEYGUARD);
1356         finishAnimationsImmediately();
1357 
1358         // WHEN the user triggers the auth bouncer
1359         mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
1360         finishAnimationsImmediately();
1361 
1362         // THEN the front scrim is updated and the KEYGUARD scrims are the same as the
1363         // KEYGUARD scrim state
1364         assertScrimAlpha(Map.of(
1365                 mScrimInFront, SEMI_TRANSPARENT,
1366                 mScrimBehind, SEMI_TRANSPARENT,
1367                 mNotificationsScrim, TRANSPARENT));
1368     }
1369 
1370     @Test
testScrimsVisible_whenShadeVisible()1371     public void testScrimsVisible_whenShadeVisible() {
1372         mScrimController.setClipsQsScrim(true);
1373         mScrimController.transitionTo(ScrimState.UNLOCKED);
1374         mScrimController.setRawPanelExpansionFraction(0.3f);
1375         // notifications scrim alpha change require calling setQsPosition
1376         mScrimController.setQsPosition(0, 300);
1377         finishAnimationsImmediately();
1378 
1379         assertScrimAlpha(Map.of(
1380                 mScrimBehind, SEMI_TRANSPARENT,
1381                 mNotificationsScrim, SEMI_TRANSPARENT,
1382                 mScrimInFront, TRANSPARENT));
1383     }
1384 
1385     @Test
testDoesntAnimate_whenUnlocking()1386     public void testDoesntAnimate_whenUnlocking() {
1387         // LightRevealScrim will animate the transition, we should only hide the keyguard scrims.
1388         ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
1389         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
1390         ScrimState.UNLOCKED.prepare(ScrimState.PULSING);
1391         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
1392 
1393         ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
1394         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
1395         ScrimState.UNLOCKED.prepare(ScrimState.AOD);
1396         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
1397 
1398         // LightRevealScrim doesn't animate when AOD is disabled. We need to use the legacy anim.
1399         ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
1400         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
1401         ScrimState.UNLOCKED.prepare(ScrimState.OFF);
1402         assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
1403     }
1404 
1405     @Test
testScrimsVisible_whenShadeVisible_clippingQs()1406     public void testScrimsVisible_whenShadeVisible_clippingQs() {
1407         mScrimController.setClipsQsScrim(true);
1408         mScrimController.transitionTo(ScrimState.UNLOCKED);
1409         mScrimController.setRawPanelExpansionFraction(0.3f);
1410         // notifications scrim alpha change require calling setQsPosition
1411         mScrimController.setQsPosition(0.5f, 300);
1412         finishAnimationsImmediately();
1413 
1414         assertScrimAlpha(Map.of(
1415                 mScrimBehind, OPAQUE,
1416                 mNotificationsScrim, SEMI_TRANSPARENT,
1417                 mScrimInFront, TRANSPARENT));
1418     }
1419 
1420     @Test
testScrimsVisible_whenShadeVisibleOnLockscreen()1421     public void testScrimsVisible_whenShadeVisibleOnLockscreen() {
1422         mScrimController.transitionTo(ScrimState.KEYGUARD);
1423         mScrimController.setQsPosition(0.25f, 300);
1424 
1425         assertScrimAlpha(Map.of(
1426                 mScrimBehind, SEMI_TRANSPARENT,
1427                 mNotificationsScrim, SEMI_TRANSPARENT,
1428                 mScrimInFront, TRANSPARENT));
1429     }
1430 
1431     @Test
testNotificationScrimTransparent_whenOnLockscreen()1432     public void testNotificationScrimTransparent_whenOnLockscreen() {
1433         mScrimController.transitionTo(ScrimState.KEYGUARD);
1434         // even if shade is not pulled down, panel has expansion of 1 on the lockscreen
1435         mScrimController.setRawPanelExpansionFraction(1);
1436         mScrimController.setQsPosition(0f, /*qs panel bottom*/ 0);
1437 
1438         assertScrimAlpha(Map.of(
1439                 mScrimBehind, SEMI_TRANSPARENT,
1440                 mNotificationsScrim, TRANSPARENT));
1441     }
1442 
1443     @Test
testNotificationScrimVisible_afterOpeningShadeFromLockscreen()1444     public void testNotificationScrimVisible_afterOpeningShadeFromLockscreen() {
1445         mScrimController.setRawPanelExpansionFraction(1);
1446         mScrimController.transitionTo(SHADE_LOCKED);
1447         finishAnimationsImmediately();
1448 
1449         assertScrimAlpha(Map.of(
1450                 mScrimBehind, OPAQUE,
1451                 mNotificationsScrim, OPAQUE));
1452     }
1453 
1454     @Test
qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress()1455     public void qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress() {
1456         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
1457         // clipping doesn't change tested logic but allows to assert scrims more in line with
1458         // their expected large screen behaviour
1459         mScrimController.setClipsQsScrim(false);
1460         mScrimController.transitionTo(SHADE_LOCKED);
1461 
1462         mScrimController.setQsPosition(1f, 100 /* value doesn't matter */);
1463         assertTintAfterExpansion(mScrimBehind, SHADE_LOCKED.getBehindTint(), /* expansion= */ 1f);
1464 
1465         mScrimController.setQsPosition(0.8f, 100 /* value doesn't matter */);
1466         // panel expansion of 0.6 means its fully transitioned with bouncer progress interpolation
1467         assertTintAfterExpansion(mScrimBehind, BOUNCER.getBehindTint(), /* expansion= */ 0.6f);
1468     }
1469 
1470     @Test
expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator()1471     public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
1472         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
1473 
1474         mScrimController.transitionTo(SHADE_LOCKED);
1475 
1476         float expansion = 0.8f;
1477         float expectedAlpha =
1478                 BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
1479         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
1480 
1481         expansion = 0.2f;
1482         expectedAlpha = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
1483         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
1484     }
1485 
1486     @Test
expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator()1487     public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
1488         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
1489 
1490         mScrimController.transitionTo(SHADE_LOCKED);
1491 
1492         float expansion = 0.8f;
1493         float expectedAlpha = ShadeInterpolation.getNotificationScrimAlpha(expansion);
1494         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
1495 
1496         expansion = 0.2f;
1497         expectedAlpha = ShadeInterpolation.getNotificationScrimAlpha(expansion);
1498         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
1499     }
1500 
1501     @Test
notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha()1502     public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() {
1503         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
1504 
1505         mScrimController.transitionTo(ScrimState.KEYGUARD);
1506 
1507         assertAlphaAfterExpansion(
1508                 mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f);
1509         assertAlphaAfterExpansion(
1510                 mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0.4f);
1511         assertAlphaAfterExpansion(
1512                 mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f);
1513 
1514         // Verify normal behavior after
1515         float expansion = 0.4f;
1516         float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
1517         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1518     }
1519 
1520     @Test
notificationAlpha_inKeyguardState_bouncerActive_usesInvertedBouncerInterpolator()1521     public void notificationAlpha_inKeyguardState_bouncerActive_usesInvertedBouncerInterpolator() {
1522         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
1523         mScrimController.setClipsQsScrim(true);
1524 
1525         mScrimController.transitionTo(ScrimState.KEYGUARD);
1526 
1527         float expansion = 0.8f;
1528         float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
1529         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1530 
1531         expansion = 0.4f;
1532         alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
1533         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1534 
1535         expansion = 0.2f;
1536         alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
1537         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1538     }
1539 
1540     @Test
notificationAlpha_inKeyguardState_bouncerNotActive_usesInvertedShadeInterpolator()1541     public void notificationAlpha_inKeyguardState_bouncerNotActive_usesInvertedShadeInterpolator() {
1542         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
1543         mScrimController.setClipsQsScrim(true);
1544 
1545         mScrimController.transitionTo(ScrimState.KEYGUARD);
1546 
1547         float expansion = 0.8f;
1548         float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
1549         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1550 
1551         expansion = 0.4f;
1552         alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
1553         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1554 
1555         expansion = 0.2f;
1556         alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
1557         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
1558     }
1559 
1560     @Test
behindTint_inKeyguardState_bouncerNotActive_usesKeyguardBehindTint()1561     public void behindTint_inKeyguardState_bouncerNotActive_usesKeyguardBehindTint() {
1562         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
1563         mScrimController.setClipsQsScrim(false);
1564 
1565         mScrimController.transitionTo(ScrimState.KEYGUARD);
1566         finishAnimationsImmediately();
1567         assertThat(mScrimBehind.getTint())
1568                 .isEqualTo(ScrimState.KEYGUARD.getBehindTint());
1569     }
1570 
1571     @Test
testNotificationTransparency_followsTransitionToFullShade()1572     public void testNotificationTransparency_followsTransitionToFullShade() {
1573         mScrimController.setClipsQsScrim(true);
1574 
1575         mScrimController.transitionTo(SHADE_LOCKED);
1576         mScrimController.setRawPanelExpansionFraction(1.0f);
1577         finishAnimationsImmediately();
1578 
1579         assertScrimTinted(Map.of(
1580                 mScrimInFront, false,
1581                 mScrimBehind, true,
1582                 mNotificationsScrim, false
1583         ));
1584 
1585         float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
1586         mScrimController.transitionTo(ScrimState.KEYGUARD);
1587         mScrimController.setRawPanelExpansionFraction(1.0f);
1588         finishAnimationsImmediately();
1589         float keyguardAlpha = mNotificationsScrim.getViewAlpha();
1590 
1591         assertScrimTinted(Map.of(
1592                 mScrimInFront, true,
1593                 mScrimBehind, true,
1594                 mNotificationsScrim, true
1595         ));
1596 
1597         float progress = 0.5f;
1598         float lsNotifProgress = 0.3f;
1599         mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
1600         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
1601                 mNotificationsScrim.getViewAlpha(), 0.2);
1602         progress = 0.0f;
1603         mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
1604         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
1605                 mNotificationsScrim.getViewAlpha(), 0.2);
1606         progress = 1.0f;
1607         mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
1608         assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
1609                 mNotificationsScrim.getViewAlpha(), 0.2);
1610     }
1611 
1612     @Test
notificationTransparency_followsNotificationScrimProgress()1613     public void notificationTransparency_followsNotificationScrimProgress() {
1614         mScrimController.transitionTo(SHADE_LOCKED);
1615         mScrimController.setRawPanelExpansionFraction(1.0f);
1616         finishAnimationsImmediately();
1617         mScrimController.transitionTo(ScrimState.KEYGUARD);
1618         mScrimController.setRawPanelExpansionFraction(1.0f);
1619         finishAnimationsImmediately();
1620 
1621         float progress = 0.5f;
1622         float notifProgress = 0.3f;
1623         mScrimController.setTransitionToFullShadeProgress(progress, notifProgress);
1624 
1625         assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(notifProgress);
1626     }
1627 
1628     @Test
notificationAlpha_qsNotClipped_alphaMatchesNotificationExpansionProgress()1629     public void notificationAlpha_qsNotClipped_alphaMatchesNotificationExpansionProgress() {
1630         mScrimController.setClipsQsScrim(false);
1631         mScrimController.transitionTo(ScrimState.KEYGUARD);
1632         // RawPanelExpansion and QsExpansion are usually used for the notification alpha
1633         // calculation.
1634         // Here we set them to non-zero values explicitly to make sure that in not clipped mode,
1635         // they are not being used even when set.
1636         mScrimController.setRawPanelExpansionFraction(0.5f);
1637         mScrimController.setQsPosition(/* expansionFraction= */ 0.5f, /* qsPanelBottomY= */ 500);
1638         finishAnimationsImmediately();
1639 
1640         float progress = 0.5f;
1641 
1642         float notificationExpansionProgress = 0f;
1643         mScrimController.setTransitionToFullShadeProgress(progress, notificationExpansionProgress);
1644         mExpect.that(mNotificationsScrim.getViewAlpha()).isEqualTo(notificationExpansionProgress);
1645 
1646         notificationExpansionProgress = 0.25f;
1647         mScrimController.setTransitionToFullShadeProgress(progress, notificationExpansionProgress);
1648         mExpect.that(mNotificationsScrim.getViewAlpha()).isEqualTo(notificationExpansionProgress);
1649 
1650         notificationExpansionProgress = 0.5f;
1651         mScrimController.setTransitionToFullShadeProgress(progress, notificationExpansionProgress);
1652         mExpect.that(mNotificationsScrim.getViewAlpha()).isEqualTo(notificationExpansionProgress);
1653 
1654         notificationExpansionProgress = 0.75f;
1655         mScrimController.setTransitionToFullShadeProgress(progress, notificationExpansionProgress);
1656         mExpect.that(mNotificationsScrim.getViewAlpha()).isEqualTo(notificationExpansionProgress);
1657 
1658         notificationExpansionProgress = 1f;
1659         mScrimController.setTransitionToFullShadeProgress(progress, notificationExpansionProgress);
1660         mExpect.that(mNotificationsScrim.getViewAlpha()).isEqualTo(notificationExpansionProgress);
1661     }
1662 
1663     @Test
setNotificationsOverScrollAmount_setsTranslationYOnNotificationsScrim()1664     public void setNotificationsOverScrollAmount_setsTranslationYOnNotificationsScrim() {
1665         int overScrollAmount = 10;
1666 
1667         mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
1668 
1669         assertThat(mNotificationsScrim.getTranslationY()).isEqualTo(overScrollAmount);
1670     }
1671 
1672     @Test
setNotificationsOverScrollAmount_doesNotSetTranslationYOnBehindScrim()1673     public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnBehindScrim() {
1674         int overScrollAmount = 10;
1675 
1676         mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
1677 
1678         assertThat(mScrimBehind.getTranslationY()).isEqualTo(0);
1679     }
1680 
1681     @Test
setNotificationsOverScrollAmount_doesNotSetTranslationYOnFrontScrim()1682     public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnFrontScrim() {
1683         int overScrollAmount = 10;
1684 
1685         mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
1686 
1687         assertThat(mScrimInFront.getTranslationY()).isEqualTo(0);
1688     }
1689 
1690     @Test
transitionToDreaming()1691     public void transitionToDreaming() {
1692         mScrimController.setRawPanelExpansionFraction(0f);
1693         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
1694         mScrimController.transitionTo(ScrimState.DREAMING);
1695         finishAnimationsImmediately();
1696 
1697         assertScrimAlpha(Map.of(
1698                 mScrimInFront, TRANSPARENT,
1699                 mNotificationsScrim, TRANSPARENT,
1700                 mScrimBehind, TRANSPARENT));
1701 
1702         assertScrimTinted(Map.of(
1703                 mScrimInFront, false,
1704                 mScrimBehind, true,
1705                 mNotificationsScrim, false
1706         ));
1707     }
1708 
1709     @Test
keyguardGoingAwayUpdateScrims()1710     public void keyguardGoingAwayUpdateScrims() {
1711         when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
1712         mScrimController.updateScrims();
1713         finishAnimationsImmediately();
1714         assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(TRANSPARENT);
1715     }
1716 
1717 
1718     @Test
setUnOccludingAnimationKeyguard()1719     public void setUnOccludingAnimationKeyguard() {
1720         mScrimController.transitionTo(ScrimState.KEYGUARD);
1721         finishAnimationsImmediately();
1722         assertThat(mNotificationsScrim.getViewAlpha())
1723                 .isWithin(0.01f).of(ScrimState.KEYGUARD.getNotifAlpha());
1724         assertThat(mNotificationsScrim.getTint())
1725                 .isEqualTo(ScrimState.KEYGUARD.getNotifTint());
1726         assertThat(mScrimBehind.getViewAlpha())
1727                 .isWithin(0.01f).of(ScrimState.KEYGUARD.getBehindAlpha());
1728         assertThat(mScrimBehind.getTint())
1729                 .isEqualTo(ScrimState.KEYGUARD.getBehindTint());
1730     }
1731 
1732     @Test
testHidesScrimFlickerInActivity()1733     public void testHidesScrimFlickerInActivity() {
1734         mScrimController.setKeyguardOccluded(true);
1735         mScrimController.transitionTo(ScrimState.KEYGUARD);
1736         finishAnimationsImmediately();
1737         assertScrimAlpha(Map.of(
1738                 mScrimInFront, TRANSPARENT,
1739                 mScrimBehind, TRANSPARENT,
1740                 mNotificationsScrim, TRANSPARENT));
1741 
1742         mScrimController.transitionTo(SHADE_LOCKED);
1743         finishAnimationsImmediately();
1744         assertScrimAlpha(Map.of(
1745                 mScrimInFront, TRANSPARENT,
1746                 mScrimBehind, TRANSPARENT,
1747                 mNotificationsScrim, TRANSPARENT));
1748     }
1749 
1750     @Test
notificationAlpha_inKeyguardState_bouncerNotActive_clipsQsScrimFalse()1751     public void notificationAlpha_inKeyguardState_bouncerNotActive_clipsQsScrimFalse() {
1752         mScrimController.setClipsQsScrim(false);
1753         mScrimController.transitionTo(ScrimState.KEYGUARD);
1754 
1755         float expansion = 0.8f;
1756         assertAlphaAfterExpansion(mNotificationsScrim, 0f, expansion);
1757     }
1758 
1759     @Test
aodStateSetsFrontScrimToNotBlend()1760     public void aodStateSetsFrontScrimToNotBlend() {
1761         mScrimController.transitionTo(ScrimState.AOD);
1762         assertFalse("Front scrim should not blend with main color",
1763                 mScrimInFront.shouldBlendWithMainColor());
1764     }
1765 
1766     @Test
applyState_unlocked_bouncerShowing()1767     public void applyState_unlocked_bouncerShowing() {
1768         mScrimController.transitionTo(ScrimState.UNLOCKED);
1769         mScrimController.setBouncerHiddenFraction(0.99f);
1770         mScrimController.setRawPanelExpansionFraction(0f);
1771         finishAnimationsImmediately();
1772         assertScrimAlpha(mScrimBehind, 0);
1773     }
1774 
1775     @Test
ignoreTransitionRequestWhileKeyguardTransitionRunning()1776     public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
1777         mScrimController.transitionTo(ScrimState.UNLOCKED);
1778         mScrimController.mPrimaryBouncerToGoneTransition.accept(
1779                 new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
1780                         TransitionState.RUNNING, "ScrimControllerTest"));
1781 
1782         // This request should not happen
1783         mScrimController.transitionTo(ScrimState.BOUNCER);
1784         assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
1785     }
1786 
1787     @Test
primaryBouncerToGoneOnFinishCallsKeyguardFadedAway()1788     public void primaryBouncerToGoneOnFinishCallsKeyguardFadedAway() {
1789         when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
1790         mScrimController.mPrimaryBouncerToGoneTransition.accept(
1791                 new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
1792                         TransitionState.FINISHED, "ScrimControllerTest"));
1793 
1794         verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
1795     }
1796 
1797     @Test
testDoNotAnimateChangeIfOccludeAnimationPlaying()1798     public void testDoNotAnimateChangeIfOccludeAnimationPlaying() {
1799         mScrimController.setOccludeAnimationPlaying(true);
1800         mScrimController.transitionTo(ScrimState.UNLOCKED);
1801 
1802         assertFalse(ScrimState.UNLOCKED.mAnimateChange);
1803     }
1804 
1805     @Test
testNotifScrimAlpha_1f_afterUnlockFinishedAndExpanded()1806     public void testNotifScrimAlpha_1f_afterUnlockFinishedAndExpanded() {
1807         mScrimController.transitionTo(ScrimState.KEYGUARD);
1808         when(mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(true);
1809         mScrimController.transitionTo(ScrimState.UNLOCKED);
1810         mScrimController.onUnlockAnimationFinished();
1811         assertAlphaAfterExpansion(mNotificationsScrim, 1f, 1f);
1812     }
1813 
assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion)1814     private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
1815         mScrimController.setRawPanelExpansionFraction(expansion);
1816         finishAnimationsImmediately();
1817         // alpha is not changing linearly thus 0.2 of leeway when asserting
1818         assertEquals(expectedAlpha, scrim.getViewAlpha(), 0.2);
1819     }
1820 
assertTintAfterExpansion(ScrimView scrim, int expectedTint, float expansion)1821     private void assertTintAfterExpansion(ScrimView scrim, int expectedTint, float expansion) {
1822         String message = "Tint test failed with expected scrim tint: "
1823                 + Integer.toHexString(expectedTint) + " and actual tint: "
1824                 + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim);
1825         mScrimController.setRawPanelExpansionFraction(expansion);
1826         finishAnimationsImmediately();
1827         assertEquals(message, expectedTint, scrim.getTint(), 0.1);
1828     }
1829 
assertScrimTinted(Map<ScrimView, Boolean> scrimToTint)1830     private void assertScrimTinted(Map<ScrimView, Boolean> scrimToTint) {
1831         scrimToTint.forEach((scrim, hasTint) -> assertScrimTint(scrim, hasTint));
1832     }
1833 
assertScrimTint(ScrimView scrim, boolean hasTint)1834     private void assertScrimTint(ScrimView scrim, boolean hasTint) {
1835         String message = "Tint test failed at state " + mScrimController.getState()
1836                 + " with scrim: " + getScrimName(scrim) + " and tint: "
1837                 + Integer.toHexString(scrim.getTint());
1838         assertEquals(message, hasTint, scrim.getTint() != Color.TRANSPARENT);
1839     }
1840 
assertScrimTint(ScrimView scrim, int expectedTint)1841     private void assertScrimTint(ScrimView scrim, int expectedTint) {
1842         String message = "Tint test failed with expected scrim tint: "
1843                 + Integer.toHexString(expectedTint) + " and actual tint: "
1844                 + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim);
1845         assertEquals(message, expectedTint, scrim.getTint(), 0.1);
1846     }
1847 
getScrimName(ScrimView scrim)1848     private String getScrimName(ScrimView scrim) {
1849         if (scrim == mScrimInFront) {
1850             return "front";
1851         } else if (scrim == mScrimBehind) {
1852             return "behind";
1853         } else if (scrim == mNotificationsScrim) {
1854             return "notifications";
1855         }
1856         return "unknown_scrim";
1857     }
1858 
1859     /**
1860      * If {@link #mNotificationsScrim} is not passed in the map
1861      * we assume it must be transparent
1862      */
assertScrimAlpha(Map<ScrimView, Integer> scrimToAlpha)1863     private void assertScrimAlpha(Map<ScrimView, Integer> scrimToAlpha) {
1864         // Check single scrim visibility.
1865         if (!scrimToAlpha.containsKey(mNotificationsScrim)) {
1866             assertScrimAlpha(mNotificationsScrim, TRANSPARENT);
1867         }
1868         scrimToAlpha.forEach((scrimView, alpha) -> assertScrimAlpha(scrimView, alpha));
1869 
1870         // When clipping, QS scrim should not affect combined visibility.
1871         if (mScrimController.getClipQsScrim() && scrimToAlpha.get(mScrimBehind) == OPAQUE) {
1872             scrimToAlpha = new HashMap<>(scrimToAlpha);
1873             scrimToAlpha.remove(mScrimBehind);
1874         }
1875 
1876         // Check combined scrim visibility.
1877         final int visibility;
1878         if (scrimToAlpha.values().contains(OPAQUE)) {
1879             visibility = OPAQUE;
1880         } else if (scrimToAlpha.values().contains(SEMI_TRANSPARENT)) {
1881             visibility = SEMI_TRANSPARENT;
1882         } else {
1883             visibility = TRANSPARENT;
1884         }
1885         assertEquals("Invalid visibility.",
1886                 visibility /* expected */,
1887                 mScrimVisibility);
1888     }
1889 
assertScrimAlpha(ScrimView scrim, int expectedAlpha)1890     private void assertScrimAlpha(ScrimView scrim, int expectedAlpha) {
1891         assertEquals("Unexpected " + getScrimName(scrim) + " scrim alpha: "
1892                         + scrim.getViewAlpha(),
1893                 expectedAlpha != TRANSPARENT /* expected */,
1894                 scrim.getViewAlpha() > TRANSPARENT /* actual */);
1895     }
1896 }
1897