Home | History | Annotate | Download | only in phone
      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.VISIBILITY_FULLY_OPAQUE;
     20 import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_TRANSPARENT;
     21 import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_SEMI_TRANSPARENT;
     22 
     23 import static org.mockito.ArgumentMatchers.any;
     24 import static org.mockito.ArgumentMatchers.anyInt;
     25 import static org.mockito.ArgumentMatchers.anyLong;
     26 import static org.mockito.Mockito.mock;
     27 import static org.mockito.Mockito.never;
     28 import static org.mockito.Mockito.reset;
     29 import static org.mockito.Mockito.spy;
     30 import static org.mockito.Mockito.verify;
     31 import static org.mockito.Mockito.verifyZeroInteractions;
     32 import static org.mockito.Mockito.when;
     33 
     34 import android.animation.Animator;
     35 import android.animation.ValueAnimator;
     36 import android.app.AlarmManager;
     37 import android.graphics.Color;
     38 import android.os.Handler;
     39 import android.os.Looper;
     40 import android.support.test.filters.SmallTest;
     41 import android.testing.AndroidTestingRunner;
     42 import android.testing.TestableLooper;
     43 import android.view.View;
     44 
     45 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
     46 import com.android.internal.util.function.TriConsumer;
     47 import com.android.keyguard.KeyguardUpdateMonitor;
     48 import com.android.systemui.SysuiTestCase;
     49 import com.android.systemui.statusbar.ScrimView;
     50 import com.android.systemui.util.wakelock.WakeLock;
     51 import com.android.systemui.utils.os.FakeHandler;
     52 
     53 import org.junit.Assert;
     54 import org.junit.Before;
     55 import org.junit.Test;
     56 import org.junit.runner.RunWith;
     57 
     58 import java.util.Arrays;
     59 import java.util.HashSet;
     60 import java.util.function.Consumer;
     61 
     62 @RunWith(AndroidTestingRunner.class)
     63 @TestableLooper.RunWithLooper
     64 @SmallTest
     65 public class ScrimControllerTest extends SysuiTestCase {
     66 
     67     private SynchronousScrimController mScrimController;
     68     private ScrimView mScrimBehind;
     69     private ScrimView mScrimInFront;
     70     private ScrimState mScrimState;
     71     private float mScrimBehindAlpha;
     72     private GradientColors mScrimInFrontColor;
     73     private int mScrimVisibility;
     74     private DozeParameters mDozeParamenters;
     75     private WakeLock mWakeLock;
     76     private boolean mAlwaysOnEnabled;
     77     private AlarmManager mAlarmManager;
     78 
     79 
     80     @Before
     81     public void setup() {
     82         mScrimBehind = spy(new ScrimView(getContext()));
     83         mScrimInFront = new ScrimView(getContext());
     84         mWakeLock = mock(WakeLock.class);
     85         mAlarmManager = mock(AlarmManager.class);
     86         mAlwaysOnEnabled = true;
     87         mDozeParamenters = mock(DozeParameters.class);
     88         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
     89         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
     90         mScrimController = new SynchronousScrimController(mScrimBehind, mScrimInFront,
     91                 (scrimState, scrimBehindAlpha, scrimInFrontColor) -> {
     92                     mScrimState = scrimState;
     93                     mScrimBehindAlpha = scrimBehindAlpha;
     94                     mScrimInFrontColor = scrimInFrontColor;
     95                 },
     96                 visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
     97     }
     98 
     99     @Test
    100     public void initialState() {
    101         Assert.assertEquals("ScrimController should start initialized",
    102                 mScrimController.getState(), ScrimState.UNINITIALIZED);
    103     }
    104 
    105     @Test
    106     public void transitionToKeyguard() {
    107         mScrimController.transitionTo(ScrimState.KEYGUARD);
    108         mScrimController.finishAnimationsImmediately();
    109         // Front scrim should be transparent
    110         // Back scrim should be visible without tint
    111         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
    112         assertScrimTint(mScrimBehind, false /* tinted */);
    113     }
    114 
    115     @Test
    116     public void transitionToAod_withRegularWallpaper() {
    117         mScrimController.transitionTo(ScrimState.AOD);
    118         mScrimController.finishAnimationsImmediately();
    119         // Front scrim should be transparent
    120         // Back scrim should be visible with tint
    121         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    122         assertScrimTint(mScrimBehind, true /* tinted */);
    123         assertScrimTint(mScrimInFront, true /* tinted */);
    124     }
    125 
    126     @Test
    127     public void transitionToAod_withAodWallpaper() {
    128         mScrimController.setWallpaperSupportsAmbientMode(true);
    129         mScrimController.transitionTo(ScrimState.AOD);
    130         mScrimController.finishAnimationsImmediately();
    131         // Front scrim should be transparent
    132         // Back scrim should be transparent
    133         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    134 
    135         // Move on to PULSING and check if the back scrim is still transparent
    136         mScrimController.transitionTo(ScrimState.PULSING);
    137         mScrimController.finishAnimationsImmediately();
    138         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    139     }
    140 
    141     @Test
    142     public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
    143         ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
    144             @Override
    145             public boolean hasLockscreenWallpaper() {
    146                 return true;
    147             }
    148         };
    149         mScrimController.setWallpaperSupportsAmbientMode(true);
    150         mScrimController.transitionTo(ScrimState.AOD);
    151         mScrimController.finishAnimationsImmediately();
    152         // Front scrim should be transparent
    153         // Back scrim should be visible with tint
    154         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    155         assertScrimTint(mScrimBehind, true /* tinted */);
    156         assertScrimTint(mScrimInFront, true /* tinted */);
    157     }
    158 
    159     @Test
    160     public void transitionToAod_withFrontAlphaUpdates() {
    161         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
    162         mScrimController.transitionTo(ScrimState.KEYGUARD);
    163         mScrimController.setAodFrontScrimAlpha(0.5f);
    164         mScrimController.finishAnimationsImmediately();
    165         // Front scrim should be transparent
    166         // Back scrim should be visible without tint
    167         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
    168 
    169         // ... but that it does take effect once we enter the AOD state.
    170         mScrimController.transitionTo(ScrimState.AOD);
    171         mScrimController.finishAnimationsImmediately();
    172         // Front scrim should be semi-transparent
    173         // Back scrim should be visible
    174         assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    175 
    176         // ... and that if we set it while we're in AOD, it does take immediate effect.
    177         mScrimController.setAodFrontScrimAlpha(1f);
    178         assertScrimVisibility(VISIBILITY_FULLY_OPAQUE, VISIBILITY_FULLY_OPAQUE);
    179 
    180         // ... and make sure we recall the previous front scrim alpha even if we transition away
    181         // for a bit.
    182         mScrimController.transitionTo(ScrimState.UNLOCKED);
    183         mScrimController.transitionTo(ScrimState.AOD);
    184         mScrimController.finishAnimationsImmediately();
    185         assertScrimVisibility(VISIBILITY_FULLY_OPAQUE, VISIBILITY_FULLY_OPAQUE);
    186 
    187         // ... and alpha updates should be completely ignored if always_on is off.
    188         // Passing it forward would mess up the wake-up transition.
    189         mAlwaysOnEnabled = false;
    190         mScrimController.transitionTo(ScrimState.UNLOCKED);
    191         mScrimController.transitionTo(ScrimState.AOD);
    192         mScrimController.finishAnimationsImmediately();
    193         mScrimController.setAodFrontScrimAlpha(0.3f);
    194         Assert.assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f);
    195         Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f);
    196 
    197         // Reset value since enums are static.
    198         mScrimController.setAodFrontScrimAlpha(0f);
    199     }
    200 
    201     @Test
    202     public void transitionToPulsing() {
    203         // Pre-condition
    204         // Need to go to AoD first because PULSING doesn't change
    205         // the back scrim opacity - otherwise it would hide AoD wallpapers.
    206         mScrimController.setWallpaperSupportsAmbientMode(false);
    207         mScrimController.transitionTo(ScrimState.AOD);
    208         mScrimController.finishAnimationsImmediately();
    209         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    210 
    211         mScrimController.transitionTo(ScrimState.PULSING);
    212         mScrimController.finishAnimationsImmediately();
    213         // Front scrim should be transparent
    214         // Back scrim should be visible with tint
    215         // Pulse callback should have been invoked
    216         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    217         assertScrimTint(mScrimBehind, true /* tinted */);
    218     }
    219 
    220     @Test
    221     public void transitionToKeyguardBouncer() {
    222         mScrimController.transitionTo(ScrimState.BOUNCER);
    223         mScrimController.finishAnimationsImmediately();
    224         // Front scrim should be transparent
    225         // Back scrim should be visible without tint
    226         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
    227         assertScrimTint(mScrimBehind, false /* tinted */);
    228     }
    229 
    230     @Test
    231     public void transitionToBouncer() {
    232         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
    233         mScrimController.finishAnimationsImmediately();
    234         // Front scrim should be transparent
    235         // Back scrim should be visible without tint
    236         assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    237         assertScrimTint(mScrimBehind, false /* tinted */);
    238     }
    239 
    240     @Test
    241     public void transitionToUnlocked() {
    242         mScrimController.setPanelExpansion(0f);
    243         mScrimController.transitionTo(ScrimState.UNLOCKED);
    244         mScrimController.finishAnimationsImmediately();
    245         // Front scrim should be transparent
    246         // Back scrim should be transparent
    247         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    248         assertScrimTint(mScrimBehind, false /* tinted */);
    249         assertScrimTint(mScrimInFront, false /* tinted */);
    250 
    251         // Back scrim should be visible after start dragging
    252         mScrimController.setPanelExpansion(0.5f);
    253         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
    254     }
    255 
    256     @Test
    257     public void scrimStateCallback() {
    258         mScrimController.transitionTo(ScrimState.UNLOCKED);
    259         mScrimController.finishAnimationsImmediately();
    260         Assert.assertEquals(mScrimState, ScrimState.UNLOCKED);
    261 
    262         mScrimController.transitionTo(ScrimState.BOUNCER);
    263         mScrimController.finishAnimationsImmediately();
    264         Assert.assertEquals(mScrimState, ScrimState.BOUNCER);
    265 
    266         mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
    267         mScrimController.finishAnimationsImmediately();
    268         Assert.assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
    269     }
    270 
    271     @Test
    272     public void panelExpansion() {
    273         mScrimController.setPanelExpansion(0f);
    274         mScrimController.setPanelExpansion(0.5f);
    275         mScrimController.transitionTo(ScrimState.UNLOCKED);
    276         mScrimController.finishAnimationsImmediately();
    277 
    278         reset(mScrimBehind);
    279         mScrimController.setPanelExpansion(0f);
    280         mScrimController.setPanelExpansion(1.0f);
    281         mScrimController.onPreDraw();
    282 
    283         Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
    284                 mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
    285 
    286         mScrimController.setPanelExpansion(0f);
    287         mScrimController.onPreDraw();
    288 
    289         Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
    290                 mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
    291     }
    292 
    293     @Test
    294     public void panelExpansionAffectsAlpha() {
    295         mScrimController.setPanelExpansion(0f);
    296         mScrimController.setPanelExpansion(0.5f);
    297         mScrimController.transitionTo(ScrimState.UNLOCKED);
    298         mScrimController.finishAnimationsImmediately();
    299 
    300         final float scrimAlpha = mScrimBehind.getViewAlpha();
    301         reset(mScrimBehind);
    302         mScrimController.setExpansionAffectsAlpha(false);
    303         mScrimController.setPanelExpansion(0.8f);
    304         verifyZeroInteractions(mScrimBehind);
    305         Assert.assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
    306                 + "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
    307 
    308         mScrimController.setExpansionAffectsAlpha(true);
    309         mScrimController.setPanelExpansion(0.1f);
    310         Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
    311                 + "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
    312     }
    313 
    314     @Test
    315     public void transitionToUnlockedFromAod() {
    316         // Simulate unlock with fingerprint
    317         mScrimController.transitionTo(ScrimState.AOD);
    318         mScrimController.setPanelExpansion(0f);
    319         mScrimController.finishAnimationsImmediately();
    320         mScrimController.transitionTo(ScrimState.UNLOCKED);
    321         // Immediately tinted after the transition starts
    322         assertScrimTint(mScrimInFront, true /* tinted */);
    323         assertScrimTint(mScrimBehind, true /* tinted */);
    324         mScrimController.finishAnimationsImmediately();
    325         // Front scrim should be transparent
    326         // Back scrim should be transparent
    327         // Neither scrims should be tinted anymore after the animation.
    328         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    329         assertScrimTint(mScrimInFront, false /* tinted */);
    330         assertScrimTint(mScrimBehind, false /* tinted */);
    331     }
    332 
    333     @Test
    334     public void scrimBlanksBeforeLeavingAod() {
    335         // Simulate unlock with fingerprint
    336         mScrimController.transitionTo(ScrimState.AOD);
    337         mScrimController.finishAnimationsImmediately();
    338         mScrimController.transitionTo(ScrimState.UNLOCKED,
    339                 new ScrimController.Callback() {
    340                     @Override
    341                     public void onDisplayBlanked() {
    342                         // Front scrim should be black in the middle of the transition
    343                         Assert.assertTrue("Scrim should be visible during transition. Alpha: "
    344                                 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
    345                         assertScrimTint(mScrimInFront, true /* tinted */);
    346                         Assert.assertSame("Scrim should be visible during transition.",
    347                                 mScrimVisibility, VISIBILITY_FULLY_OPAQUE);
    348                     }
    349                 });
    350         mScrimController.finishAnimationsImmediately();
    351     }
    352 
    353     @Test
    354     public void scrimBlanksWhenUnlockingFromPulse() {
    355         boolean[] blanked = {false};
    356         // Simulate unlock with fingerprint
    357         mScrimController.transitionTo(ScrimState.PULSING);
    358         mScrimController.finishAnimationsImmediately();
    359         mScrimController.transitionTo(ScrimState.UNLOCKED,
    360                 new ScrimController.Callback() {
    361                     @Override
    362                     public void onDisplayBlanked() {
    363                         blanked[0] = true;
    364                     }
    365                 });
    366         mScrimController.finishAnimationsImmediately();
    367         Assert.assertTrue("Scrim should blank when unlocking from pulse.", blanked[0]);
    368     }
    369 
    370     @Test
    371     public void testScrimCallback() {
    372         int[] callOrder = {0, 0, 0};
    373         int[] currentCall = {0};
    374         mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
    375             @Override
    376             public void onStart() {
    377                 callOrder[0] = ++currentCall[0];
    378             }
    379 
    380             @Override
    381             public void onDisplayBlanked() {
    382                 callOrder[1] = ++currentCall[0];
    383             }
    384 
    385             @Override
    386             public void onFinished() {
    387                 callOrder[2] = ++currentCall[0];
    388             }
    389         });
    390         mScrimController.finishAnimationsImmediately();
    391         Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
    392         Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
    393         Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
    394     }
    395 
    396     @Test
    397     public void testScrimCallbacksWithoutAmbientDisplay() {
    398         mAlwaysOnEnabled = false;
    399         testScrimCallback();
    400     }
    401 
    402     @Test
    403     public void testScrimCallbackCancelled() {
    404         boolean[] cancelledCalled = {false};
    405         mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
    406             @Override
    407             public void onCancelled() {
    408                 cancelledCalled[0] = true;
    409             }
    410         });
    411         mScrimController.transitionTo(ScrimState.PULSING);
    412         Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
    413     }
    414 
    415     @Test
    416     public void testHoldsWakeLock_whenAOD() {
    417         mScrimController.transitionTo(ScrimState.AOD);
    418         verify(mWakeLock).acquire();
    419         verify(mWakeLock, never()).release();
    420         mScrimController.finishAnimationsImmediately();
    421         verify(mWakeLock).release();
    422     }
    423 
    424     @Test
    425     public void testDoesNotHoldWakeLock_whenUnlocking() {
    426         mScrimController.transitionTo(ScrimState.UNLOCKED);
    427         mScrimController.finishAnimationsImmediately();
    428         verifyZeroInteractions(mWakeLock);
    429     }
    430 
    431     @Test
    432     public void testCallbackInvokedOnSameStateTransition() {
    433         mScrimController.transitionTo(ScrimState.UNLOCKED);
    434         mScrimController.finishAnimationsImmediately();
    435         ScrimController.Callback callback = mock(ScrimController.Callback.class);
    436         mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
    437         verify(callback).onFinished();
    438     }
    439 
    440     @Test
    441     public void testHoldsAodWallpaperAnimationLock() {
    442         // Pre-conditions
    443         mScrimController.transitionTo(ScrimState.AOD);
    444         mScrimController.finishAnimationsImmediately();
    445         reset(mWakeLock);
    446 
    447         mScrimController.onHideWallpaperTimeout();
    448         verify(mWakeLock).acquire();
    449         verify(mWakeLock, never()).release();
    450         mScrimController.finishAnimationsImmediately();
    451         verify(mWakeLock).release();
    452     }
    453 
    454     @Test
    455     public void testWillHideAodWallpaper() {
    456         mScrimController.setWallpaperSupportsAmbientMode(true);
    457         mScrimController.transitionTo(ScrimState.AOD);
    458         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
    459         mScrimController.transitionTo(ScrimState.KEYGUARD);
    460         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
    461     }
    462 
    463     @Test
    464     public void testConservesExpansionOpacityAfterTransition() {
    465         mScrimController.transitionTo(ScrimState.UNLOCKED);
    466         mScrimController.setPanelExpansion(0.5f);
    467         mScrimController.finishAnimationsImmediately();
    468 
    469         final float expandedAlpha = mScrimBehind.getViewAlpha();
    470 
    471         mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
    472         mScrimController.finishAnimationsImmediately();
    473         mScrimController.transitionTo(ScrimState.UNLOCKED);
    474         mScrimController.finishAnimationsImmediately();
    475 
    476         Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
    477                 expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
    478     }
    479 
    480     @Test
    481     public void cancelsOldAnimationBeforeBlanking() {
    482         mScrimController.transitionTo(ScrimState.AOD);
    483         mScrimController.finishAnimationsImmediately();
    484         // Consume whatever value we had before
    485         mScrimController.wasAnimationJustCancelled();
    486 
    487         mScrimController.transitionTo(ScrimState.KEYGUARD);
    488         mScrimController.finishAnimationsImmediately();
    489         Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
    490     }
    491 
    492     /**
    493      * Number of visible notifications affects scrim opacity.
    494      */
    495     @Test
    496     public void testNotificationDensity() {
    497         mScrimController.transitionTo(ScrimState.KEYGUARD);
    498         mScrimController.finishAnimationsImmediately();
    499 
    500         mScrimController.setNotificationCount(0);
    501         mScrimController.finishAnimationsImmediately();
    502         Assert.assertEquals("lower density when no notifications",
    503                 ScrimController.GRADIENT_SCRIM_ALPHA,  mScrimBehind.getViewAlpha(), 0.01f);
    504 
    505         mScrimController.setNotificationCount(3);
    506         mScrimController.finishAnimationsImmediately();
    507         Assert.assertEquals("stronger density when notifications are visible",
    508                 ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,  mScrimBehind.getViewAlpha(), 0.01f);
    509     }
    510 
    511     /**
    512      * Moving from/to states conserves old notification density.
    513      */
    514     @Test
    515     public void testConservesNotificationDensity() {
    516         testConservesNotificationDensity(0 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA);
    517         testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY);
    518     }
    519 
    520     @Test
    521     public void testScrimFocus() {
    522         mScrimController.transitionTo(ScrimState.AOD);
    523         Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
    524         Assert.assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable());
    525 
    526         mScrimController.transitionTo(ScrimState.KEYGUARD);
    527         Assert.assertTrue("Should be focusable on keyguard", mScrimBehind.isFocusable());
    528         Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
    529     }
    530 
    531     @Test
    532     public void testHidesShowWhenLockedActivity() {
    533         mScrimController.setWallpaperSupportsAmbientMode(true);
    534         mScrimController.setKeyguardOccluded(true);
    535         mScrimController.transitionTo(ScrimState.AOD);
    536         mScrimController.finishAnimationsImmediately();
    537         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    538 
    539         mScrimController.transitionTo(ScrimState.PULSING);
    540         mScrimController.finishAnimationsImmediately();
    541         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    542     }
    543 
    544     @Test
    545     public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
    546         mScrimController.setWallpaperSupportsAmbientMode(true);
    547         mScrimController.transitionTo(ScrimState.AOD);
    548         mScrimController.finishAnimationsImmediately();
    549         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
    550 
    551         mScrimController.setKeyguardOccluded(true);
    552         mScrimController.finishAnimationsImmediately();
    553         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
    554     }
    555 
    556     @Test
    557     public void testEatsTouchEvent() {
    558         HashSet<ScrimState> eatsTouches =
    559                 new HashSet<>(Arrays.asList(ScrimState.AOD, ScrimState.PULSING));
    560         for (ScrimState state : ScrimState.values()) {
    561             if (state == ScrimState.UNINITIALIZED) {
    562                 continue;
    563             }
    564             mScrimController.transitionTo(state);
    565             mScrimController.finishAnimationsImmediately();
    566             Assert.assertEquals("Should be clickable unless AOD or PULSING, was: " + state,
    567                     mScrimBehind.getViewAlpha() != 0 && !eatsTouches.contains(state),
    568                     mScrimBehind.isClickable());
    569         }
    570     }
    571 
    572     @Test
    573     public void testAnimatesTransitionToAod() {
    574         when(mDozeParamenters.shouldControlScreenOff()).thenReturn(false);
    575         ScrimState.AOD.prepare(ScrimState.KEYGUARD);
    576         Assert.assertFalse("No animation when ColorFade kicks in",
    577                 ScrimState.AOD.getAnimateChange());
    578 
    579         reset(mDozeParamenters);
    580         when(mDozeParamenters.shouldControlScreenOff()).thenReturn(true);
    581         ScrimState.AOD.prepare(ScrimState.KEYGUARD);
    582         Assert.assertTrue("Animate scrims when ColorFade won't be triggered",
    583                 ScrimState.AOD.getAnimateChange());
    584     }
    585 
    586     @Test
    587     public void testViewsDontHaveFocusHighlight() {
    588         Assert.assertFalse("Scrim shouldn't have focus highlight",
    589                 mScrimInFront.getDefaultFocusHighlightEnabled());
    590         Assert.assertFalse("Scrim shouldn't have focus highlight",
    591                 mScrimBehind.getDefaultFocusHighlightEnabled());
    592     }
    593 
    594     /**
    595      * Conserves old notification density after leaving state and coming back.
    596      *
    597      * @param count How many notification.
    598      * @param expectedAlpha Expected alpha.
    599      */
    600     private void testConservesNotificationDensity(int count, float expectedAlpha) {
    601         mScrimController.setNotificationCount(count);
    602         mScrimController.transitionTo(ScrimState.UNLOCKED);
    603         mScrimController.finishAnimationsImmediately();
    604 
    605         mScrimController.transitionTo(ScrimState.KEYGUARD);
    606         mScrimController.finishAnimationsImmediately();
    607 
    608         Assert.assertEquals("Doesn't respect notification busyness after transition",
    609                 expectedAlpha,  mScrimBehind.getViewAlpha(), 0.01f);
    610     }
    611 
    612     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
    613         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
    614         final String name = scrimView == mScrimInFront ? "front" : "back";
    615         Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
    616                 +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
    617                 tinted, viewIsTinted);
    618     }
    619 
    620     private void assertScrimVisibility(int inFront, int behind) {
    621         boolean inFrontVisible = inFront != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
    622         boolean behindVisible = behind != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
    623         Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
    624                 + mScrimInFront.getViewAlpha(), inFrontVisible, mScrimInFront.getViewAlpha() > 0);
    625         Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
    626                 + mScrimBehind.getViewAlpha(), behindVisible, mScrimBehind.getViewAlpha() > 0);
    627 
    628         final int visibility;
    629         if (inFront == VISIBILITY_FULLY_OPAQUE || behind == VISIBILITY_FULLY_OPAQUE) {
    630             visibility = VISIBILITY_FULLY_OPAQUE;
    631         } else if (inFront > VISIBILITY_FULLY_TRANSPARENT || behind > VISIBILITY_FULLY_TRANSPARENT) {
    632             visibility = VISIBILITY_SEMI_TRANSPARENT;
    633         } else {
    634             visibility = VISIBILITY_FULLY_TRANSPARENT;
    635         }
    636         Assert.assertEquals("Invalid visibility.", visibility, mScrimVisibility);
    637     }
    638 
    639     /**
    640      * Special version of ScrimController where animations have 0 duration for test purposes.
    641      */
    642     private class SynchronousScrimController extends ScrimController {
    643 
    644         private FakeHandler mHandler;
    645         private boolean mAnimationCancelled;
    646         boolean mOnPreDrawCalled;
    647 
    648         SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
    649                 TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
    650                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
    651                 AlarmManager alarmManager) {
    652             super(scrimBehind, scrimInFront, scrimStateListener, scrimVisibleListener,
    653                     dozeParameters, alarmManager);
    654             mHandler = new FakeHandler(Looper.myLooper());
    655         }
    656 
    657         @Override
    658         public boolean onPreDraw() {
    659             mOnPreDrawCalled = true;
    660             return super.onPreDraw();
    661         }
    662 
    663         void finishAnimationsImmediately() {
    664             boolean[] animationFinished = {false};
    665             setOnAnimationFinished(()-> animationFinished[0] = true);
    666 
    667             // Execute code that will trigger animations.
    668             onPreDraw();
    669 
    670             // Force finish screen blanking.
    671             mHandler.dispatchQueuedMessages();
    672             // Force finish all animations.
    673             endAnimation(mScrimBehind, TAG_KEY_ANIM);
    674             endAnimation(mScrimInFront, TAG_KEY_ANIM);
    675 
    676             if (!animationFinished[0]) {
    677                 throw new IllegalStateException("Animation never finished");
    678             }
    679         }
    680 
    681         boolean wasAnimationJustCancelled() {
    682             final boolean wasCancelled = mAnimationCancelled;
    683             mAnimationCancelled = false;
    684             return wasCancelled;
    685         }
    686 
    687         private void endAnimation(View scrimView, int tag) {
    688             Animator animator = (Animator) scrimView.getTag(tag);
    689             if (animator != null) {
    690                 animator.end();
    691             }
    692         }
    693 
    694         @Override
    695         protected void cancelAnimator(ValueAnimator previousAnimator) {
    696             super.cancelAnimator(previousAnimator);
    697             mAnimationCancelled = true;
    698         }
    699 
    700         @Override
    701         protected Handler getHandler() {
    702             return mHandler;
    703         }
    704 
    705         @Override
    706         protected WakeLock createWakeLock() {
    707             return mWakeLock;
    708         }
    709 
    710         /**
    711          * Do not wait for a frame since we're in a test environment.
    712          * @param callback What to execute.
    713          */
    714         @Override
    715         protected void doOnTheNextFrame(Runnable callback) {
    716             callback.run();
    717         }
    718     }
    719 
    720 }
    721