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