1 /*
2  * Copyright (C) 2014 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.util.kotlin.JavaAdapterKt.collectFlow;
20 
21 import static java.lang.Float.isNaN;
22 
23 import android.animation.Animator;
24 import android.animation.AnimatorListenerAdapter;
25 import android.animation.ValueAnimator;
26 import android.annotation.IntDef;
27 import android.app.AlarmManager;
28 import android.graphics.Color;
29 import android.os.Handler;
30 import android.os.Trace;
31 import android.util.Log;
32 import android.util.MathUtils;
33 import android.util.Pair;
34 import android.view.View;
35 import android.view.ViewTreeObserver;
36 import android.view.animation.DecelerateInterpolator;
37 import android.view.animation.Interpolator;
38 
39 import androidx.annotation.FloatRange;
40 import androidx.annotation.Nullable;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
44 import com.android.internal.graphics.ColorUtils;
45 import com.android.internal.util.ContrastColorUtil;
46 import com.android.internal.util.function.TriConsumer;
47 import com.android.keyguard.BouncerPanelExpansionCalculator;
48 import com.android.keyguard.KeyguardUpdateMonitor;
49 import com.android.keyguard.KeyguardUpdateMonitorCallback;
50 import com.android.settingslib.Utils;
51 import com.android.systemui.CoreStartable;
52 import com.android.systemui.DejankUtils;
53 import com.android.systemui.Dumpable;
54 import com.android.systemui.R;
55 import com.android.systemui.animation.ShadeInterpolation;
56 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
57 import com.android.systemui.dagger.SysUISingleton;
58 import com.android.systemui.dagger.qualifiers.Main;
59 import com.android.systemui.dock.DockManager;
60 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
61 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
62 import com.android.systemui.keyguard.shared.model.ScrimAlpha;
63 import com.android.systemui.keyguard.shared.model.TransitionState;
64 import com.android.systemui.keyguard.shared.model.TransitionStep;
65 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
66 import com.android.systemui.scrim.ScrimView;
67 import com.android.systemui.shade.ShadeViewController;
68 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
69 import com.android.systemui.statusbar.notification.stack.ViewState;
70 import com.android.systemui.statusbar.policy.ConfigurationController;
71 import com.android.systemui.statusbar.policy.KeyguardStateController;
72 import com.android.systemui.util.AlarmTimeout;
73 import com.android.systemui.util.kotlin.JavaAdapter;
74 import com.android.systemui.util.wakelock.DelayedWakeLock;
75 import com.android.systemui.util.wakelock.WakeLock;
76 import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
77 
78 import java.io.PrintWriter;
79 import java.lang.annotation.Retention;
80 import java.lang.annotation.RetentionPolicy;
81 import java.util.concurrent.Executor;
82 import java.util.function.Consumer;
83 
84 import javax.inject.Inject;
85 
86 import kotlinx.coroutines.CoroutineDispatcher;
87 
88 /**
89  * Controls both the scrim behind the notifications and in front of the notifications (when a
90  * security method gets shown).
91  */
92 @SysUISingleton
93 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable,
94         CoreStartable {
95 
96     static final String TAG = "ScrimController";
97     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
98 
99     // debug mode colors scrims with below debug colors, irrespectively of which state they're in
100     public static final boolean DEBUG_MODE = false;
101 
102     public static final int DEBUG_NOTIFICATIONS_TINT = Color.RED;
103     public static final int DEBUG_FRONT_TINT = Color.GREEN;
104     public static final int DEBUG_BEHIND_TINT = Color.BLUE;
105 
106     /**
107      * General scrim animation duration.
108      */
109     public static final long ANIMATION_DURATION = 220;
110     /**
111      * Longer duration, currently only used when going to AOD.
112      */
113     public static final long ANIMATION_DURATION_LONG = 1000;
114     /**
115      * When both scrims have 0 alpha.
116      */
117     public static final int TRANSPARENT = 0;
118     /**
119      * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.)
120      */
121     public static final int SEMI_TRANSPARENT = 1;
122     /**
123      * When at least 1 scrim is fully opaque (alpha set to 1.)
124      */
125     public static final int OPAQUE = 2;
126     private boolean mClipsQsScrim;
127 
128     /**
129      * Whether an activity is launching over the lockscreen. During the launch animation, we want to
130      * delay certain scrim changes until after the animation ends.
131      */
132     private boolean mOccludeAnimationPlaying = false;
133 
134     /**
135      * The amount of progress we are currently in if we're transitioning to the full shade.
136      * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
137      * shade.
138      */
139     private float mTransitionToFullShadeProgress;
140 
141     /**
142      * Same as {@link #mTransitionToFullShadeProgress}, but specifically for the notifications scrim
143      * on the lock screen.
144      *
145      * On split shade lock screen we want the different scrims to fade in at different times and
146      * rates.
147      */
148     private float mTransitionToLockScreenFullShadeNotificationsProgress;
149 
150     /**
151      * If we're currently transitioning to the full shade.
152      */
153     private boolean mTransitioningToFullShade;
154 
155     /**
156      * The percentage of the bouncer which is hidden. If 1, the bouncer is completely hidden. If
157      * 0, the bouncer is visible.
158      */
159     @FloatRange(from = 0, to = 1)
160     private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN;
161 
162     @IntDef(prefix = {"VISIBILITY_"}, value = {
163             TRANSPARENT,
164             SEMI_TRANSPARENT,
165             OPAQUE
166     })
167     @Retention(RetentionPolicy.SOURCE)
168     public @interface ScrimVisibility {
169     }
170 
171     /**
172      * Default alpha value for most scrims.
173      */
174     protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f;
175     /**
176      * Scrim opacity when the phone is about to wake-up.
177      */
178     public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f;
179 
180     /**
181      * The default scrim under the shade and dialogs.
182      * This should not be lower than 0.54, otherwise we won't pass GAR.
183      */
184     public static final float BUSY_SCRIM_ALPHA = 1f;
185 
186     /**
187      * Scrim opacity that can have text on top.
188      */
189     public static final float GAR_SCRIM_ALPHA = 0.6f;
190 
191     static final int TAG_KEY_ANIM = R.id.scrim;
192     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
193     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
194     private static final float NOT_INITIALIZED = -1;
195 
196     private ScrimState mState = ScrimState.UNINITIALIZED;
197 
198     private ScrimView mScrimInFront;
199     private ScrimView mNotificationsScrim;
200     private ScrimView mScrimBehind;
201 
202     private Runnable mScrimBehindChangeRunnable;
203 
204     private final KeyguardStateController mKeyguardStateController;
205     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
206     private final DozeParameters mDozeParameters;
207     private final DockManager mDockManager;
208     private final AlarmTimeout mTimeTicker;
209     private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
210     private final Handler mHandler;
211     private final Executor mMainExecutor;
212     private final JavaAdapter mJavaAdapter;
213     private final ScreenOffAnimationController mScreenOffAnimationController;
214     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
215     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
216 
217     private GradientColors mColors;
218     private boolean mNeedsDrawableColorUpdate;
219 
220     private float mAdditionalScrimBehindAlphaKeyguard = 0f;
221     // Combined scrim behind keyguard alpha of default scrim + additional scrim
222     // (if wallpaper dimming is applied).
223     private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
224     private final float mDefaultScrimAlpha;
225 
226     private float mRawPanelExpansionFraction;
227     private float mPanelScrimMinFraction;
228     // Calculated based on mRawPanelExpansionFraction and mPanelScrimMinFraction
229     private float mPanelExpansionFraction = 1f; // Assume shade is expanded during initialization
230     private float mQsExpansion;
231     private boolean mQsBottomVisible;
232     private boolean mAnimatingPanelExpansionOnUnlock; // don't animate scrim
233 
234     private boolean mDarkenWhileDragging;
235     private boolean mExpansionAffectsAlpha = true;
236     private boolean mAnimateChange;
237     private boolean mUpdatePending;
238     private long mAnimationDuration = -1;
239     private long mAnimationDelay;
240     private Animator.AnimatorListener mAnimatorListener;
241     private final Interpolator mInterpolator = new DecelerateInterpolator();
242 
243     private float mInFrontAlpha = NOT_INITIALIZED;
244     private float mBehindAlpha = NOT_INITIALIZED;
245     private float mNotificationsAlpha = NOT_INITIALIZED;
246 
247     private int mInFrontTint;
248     private int mBehindTint;
249     private int mNotificationsTint;
250 
251     private boolean mWallpaperVisibilityTimedOut;
252     private int mScrimsVisibility;
253     private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
254     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
255     private Consumer<Integer> mScrimVisibleListener;
256     private boolean mBlankScreen;
257     private boolean mScreenBlankingCallbackCalled;
258     private Callback mCallback;
259     private boolean mWallpaperSupportsAmbientMode;
260     private boolean mScreenOn;
261     private boolean mTransparentScrimBackground;
262 
263     // Scrim blanking callbacks
264     private Runnable mPendingFrameCallback;
265     private Runnable mBlankingTransitionRunnable;
266 
267     private final WakeLock mWakeLock;
268     private boolean mWakeLockHeld;
269     private boolean mKeyguardOccluded;
270 
271     private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
272     private final WallpaperRepository mWallpaperRepository;
273     private CoroutineDispatcher mMainDispatcher;
274     private boolean mIsBouncerToGoneTransitionRunning = false;
275     private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
276     private final Consumer<ScrimAlpha> mScrimAlphaConsumer =
277             (ScrimAlpha alphas) -> {
278                 mInFrontAlpha = alphas.getFrontAlpha();
279                 mScrimInFront.setViewAlpha(mInFrontAlpha);
280 
281                 mNotificationsAlpha = alphas.getNotificationsAlpha();
282                 mNotificationsScrim.setViewAlpha(mNotificationsAlpha);
283 
284                 mBehindAlpha = alphas.getBehindAlpha();
285                 mScrimBehind.setViewAlpha(mBehindAlpha);
286             };
287 
288     Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
289 
290     @Inject
ScrimController( LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, ConfigurationController configurationController, @Main Executor mainExecutor, JavaAdapter javaAdapter, ScreenOffAnimationController screenOffAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, WallpaperRepository wallpaperRepository, @Main CoroutineDispatcher mainDispatcher, LargeScreenShadeInterpolator largeScreenShadeInterpolator)291     public ScrimController(
292             LightBarController lightBarController,
293             DozeParameters dozeParameters,
294             AlarmManager alarmManager,
295             KeyguardStateController keyguardStateController,
296             DelayedWakeLock.Builder delayedWakeLockBuilder,
297             Handler handler,
298             KeyguardUpdateMonitor keyguardUpdateMonitor,
299             DockManager dockManager,
300             ConfigurationController configurationController,
301             @Main Executor mainExecutor,
302             JavaAdapter javaAdapter,
303             ScreenOffAnimationController screenOffAnimationController,
304             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
305             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
306             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
307             KeyguardTransitionInteractor keyguardTransitionInteractor,
308             WallpaperRepository wallpaperRepository,
309             @Main CoroutineDispatcher mainDispatcher,
310             LargeScreenShadeInterpolator largeScreenShadeInterpolator) {
311         mScrimStateListener = lightBarController::setScrimState;
312         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
313         mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
314 
315         mKeyguardStateController = keyguardStateController;
316         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
317         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
318         mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
319         mHandler = handler;
320         mMainExecutor = mainExecutor;
321         mJavaAdapter = javaAdapter;
322         mScreenOffAnimationController = screenOffAnimationController;
323         mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
324                 "hide_aod_wallpaper", mHandler);
325         mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build();
326         // Scrim alpha is initially set to the value on the resource but might be changed
327         // to make sure that text on top of it is legible.
328         mDozeParameters = dozeParameters;
329         mDockManager = dockManager;
330         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
331         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
332             @Override
333             public void onKeyguardFadingAwayChanged() {
334                 setKeyguardFadingAway(keyguardStateController.isKeyguardFadingAway(),
335                         keyguardStateController.getKeyguardFadingAwayDuration());
336             }
337         });
338         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
339         configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
340             @Override
341             public void onThemeChanged() {
342                 ScrimController.this.onThemeChanged();
343             }
344 
345             @Override
346             public void onUiModeChanged() {
347                 ScrimController.this.onThemeChanged();
348             }
349         });
350         mColors = new GradientColors();
351         mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
352         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
353         mWallpaperRepository = wallpaperRepository;
354         mMainDispatcher = mainDispatcher;
355     }
356 
357     @Override
start()358     public void start() {
359         mJavaAdapter.alwaysCollectFlow(
360                 mWallpaperRepository.getWallpaperSupportsAmbientMode(),
361                 this::setWallpaperSupportsAmbientMode);
362     }
363 
364     /**
365      * Attach the controller to the supplied views.
366      */
attachViews(ScrimView behindScrim, ScrimView notificationsScrim, ScrimView scrimInFront)367     public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim,
368                             ScrimView scrimInFront) {
369         mNotificationsScrim = notificationsScrim;
370         mScrimBehind = behindScrim;
371         mScrimInFront = scrimInFront;
372         updateThemeColors();
373         mNotificationsScrim.setScrimName(getScrimName(mNotificationsScrim));
374         mScrimBehind.setScrimName(getScrimName(mScrimBehind));
375         mScrimInFront.setScrimName(getScrimName(mScrimInFront));
376 
377         behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
378         mNotificationsScrim.enableRoundedCorners(true);
379 
380         if (mScrimBehindChangeRunnable != null) {
381             mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
382             mScrimBehindChangeRunnable = null;
383         }
384 
385         final ScrimState[] states = ScrimState.values();
386         for (int i = 0; i < states.length; i++) {
387             states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
388             states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
389             states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
390         }
391 
392         mScrimBehind.setDefaultFocusHighlightEnabled(false);
393         mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
394         mScrimInFront.setDefaultFocusHighlightEnabled(false);
395         mTransparentScrimBackground = notificationsScrim.getResources()
396                 .getBoolean(R.bool.notification_scrim_transparent);
397         updateScrims();
398         mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
399 
400         // prepare() sets proper initial values for most states
401         for (ScrimState state : ScrimState.values()) {
402             state.prepare(state);
403         }
404 
405         // Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure
406         // to report back that keyguard has faded away. This fixes cases where the scrim state was
407         // rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl
408         mPrimaryBouncerToGoneTransition =
409                 (TransitionStep step) -> {
410                     TransitionState state = step.getTransitionState();
411 
412                     mIsBouncerToGoneTransitionRunning = state == TransitionState.RUNNING;
413 
414                     if (state == TransitionState.STARTED) {
415                         setExpansionAffectsAlpha(false);
416                         transitionTo(ScrimState.UNLOCKED);
417                     }
418 
419                     if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
420                         setExpansionAffectsAlpha(true);
421                         if (mKeyguardStateController.isKeyguardFadingAway()) {
422                             mStatusBarKeyguardViewManager.onKeyguardFadedAway();
423                         }
424                         dispatchScrimsVisible();
425                     }
426                 };
427 
428         collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
429                 mPrimaryBouncerToGoneTransition, mMainDispatcher);
430         collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
431                 mScrimAlphaConsumer, mMainDispatcher);
432     }
433 
434     // TODO(b/270984686) recompute scrim height accurately, based on shade contents.
435     /** Set corner radius of the bottom edge of the Notification scrim. */
setNotificationBottomRadius(float radius)436     public void setNotificationBottomRadius(float radius) {
437         if (mNotificationsScrim == null) {
438             return;
439         }
440         mNotificationsScrim.setBottomEdgeRadius(radius);
441     }
442 
443     /** Sets corner radius of scrims. */
setScrimCornerRadius(int radius)444     public void setScrimCornerRadius(int radius) {
445         if (mScrimBehind == null || mNotificationsScrim == null) {
446             return;
447         }
448         mScrimBehind.setCornerRadius(radius);
449         mNotificationsScrim.setCornerRadius(radius);
450     }
451 
setScrimVisibleListener(Consumer<Integer> listener)452     void setScrimVisibleListener(Consumer<Integer> listener) {
453         mScrimVisibleListener = listener;
454     }
455 
transitionTo(ScrimState state)456     public void transitionTo(ScrimState state) {
457         transitionTo(state, null);
458     }
459 
transitionTo(ScrimState state, Callback callback)460     public void transitionTo(ScrimState state, Callback callback) {
461         if (mIsBouncerToGoneTransitionRunning) {
462             Log.i(TAG, "Skipping transition to: " + state
463                     + " while mIsBouncerToGoneTransitionRunning");
464             return;
465         }
466         if (state == mState) {
467             // Call the callback anyway, unless it's already enqueued
468             if (callback != null && mCallback != callback) {
469                 callback.onFinished();
470             }
471             return;
472         } else if (DEBUG) {
473             Log.d(TAG, "State changed to: " + state);
474         }
475 
476         if (state == ScrimState.UNINITIALIZED) {
477             throw new IllegalArgumentException("Cannot change to UNINITIALIZED.");
478         }
479 
480         final ScrimState oldState = mState;
481         mState = state;
482         Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
483 
484         if (mCallback != null) {
485             mCallback.onCancelled();
486         }
487         mCallback = callback;
488 
489         state.prepare(oldState);
490         mScreenBlankingCallbackCalled = false;
491         mAnimationDelay = 0;
492         mBlankScreen = state.getBlanksScreen();
493         mAnimateChange = state.getAnimateChange();
494         mAnimationDuration = state.getAnimationDuration();
495 
496         applyState();
497 
498         // Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
499         // We need to disable focus otherwise AOD would end up with a gray overlay.
500         mScrimInFront.setFocusable(!state.isLowPowerState());
501         mScrimBehind.setFocusable(!state.isLowPowerState());
502         mNotificationsScrim.setFocusable(!state.isLowPowerState());
503 
504         mScrimInFront.setBlendWithMainColor(state.shouldBlendWithMainColor());
505 
506         // Cancel blanking transitions that were pending before we requested a new state
507         if (mPendingFrameCallback != null) {
508             mScrimBehind.removeCallbacks(mPendingFrameCallback);
509             mPendingFrameCallback = null;
510         }
511         if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) {
512             mHandler.removeCallbacks(mBlankingTransitionRunnable);
513             mBlankingTransitionRunnable = null;
514         }
515 
516         // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary
517         // to do the same when you're just showing the brightness mirror.
518         mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR;
519 
520         // The device might sleep if it's entering AOD, we need to make sure that
521         // the animation plays properly until the last frame.
522         // It's important to avoid holding the wakelock unless necessary because
523         // WakeLock#aqcuire will trigger an IPC and will cause jank.
524         if (mState.isLowPowerState()) {
525             holdWakeLock();
526         }
527 
528         // AOD wallpapers should fade away after a while.
529         // Docking pulses may take a long time, wallpapers should also fade away after a while.
530         mWallpaperVisibilityTimedOut = false;
531         if (shouldFadeAwayWallpaper()) {
532             DejankUtils.postAfterTraversal(() -> {
533                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
534                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
535             });
536         } else {
537             DejankUtils.postAfterTraversal(mTimeTicker::cancel);
538         }
539 
540         if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
541             mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY;
542             scheduleUpdate();
543         } else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING)  // leaving doze
544                 && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
545                 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
546             // Scheduling a frame isn't enough when:
547             //  • Leaving doze and we need to modify scrim color immediately
548             //  • ColorFade will not kick-in and scrim cannot wait for pre-draw.
549             onPreDraw();
550         } else {
551             // Schedule a frame
552             scheduleUpdate();
553         }
554 
555         dispatchBackScrimState(mScrimBehind.getViewAlpha());
556     }
557 
shouldFadeAwayWallpaper()558     private boolean shouldFadeAwayWallpaper() {
559         if (!mWallpaperSupportsAmbientMode) {
560             return false;
561         }
562 
563         if (mState == ScrimState.AOD
564                 && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) {
565             return true;
566         }
567 
568         return false;
569     }
570 
getState()571     public ScrimState getState() {
572         return mState;
573     }
574 
575     /**
576      * Sets the additional scrim behind alpha keyguard that would be blended with the default scrim
577      * by applying alpha composition on both values.
578      *
579      * @param additionalScrimAlpha alpha value of additional scrim behind alpha keyguard.
580      */
setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha)581     protected void setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha) {
582         mAdditionalScrimBehindAlphaKeyguard = additionalScrimAlpha;
583     }
584 
585     /**
586      * Applies alpha composition to the default scrim behind alpha keyguard and the additional
587      * scrim alpha, and sets this value to the scrim behind alpha keyguard.
588      * This is used to apply additional keyguard dimming on top of the default scrim alpha value.
589      */
applyCompositeAlphaOnScrimBehindKeyguard()590     protected void applyCompositeAlphaOnScrimBehindKeyguard() {
591         int compositeAlpha = ColorUtils.compositeAlpha(
592                 (int) (255 * mAdditionalScrimBehindAlphaKeyguard),
593                 (int) (255 * KEYGUARD_SCRIM_ALPHA));
594         float keyguardScrimAlpha = (float) compositeAlpha / 255;
595         setScrimBehindValues(keyguardScrimAlpha);
596     }
597 
598     /**
599      * Sets the scrim behind alpha keyguard values. This is how much the keyguard will be dimmed.
600      *
601      * @param scrimBehindAlphaKeyguard alpha value of the scrim behind
602      */
setScrimBehindValues(float scrimBehindAlphaKeyguard)603     private void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
604         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
605         ScrimState[] states = ScrimState.values();
606         for (int i = 0; i < states.length; i++) {
607             states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard);
608         }
609         scheduleUpdate();
610     }
611 
612     /** This is used by the predictive back gesture animation to scale the Shade. */
applyBackScaling(float scale)613     public void applyBackScaling(float scale) {
614         mNotificationsScrim.setScaleX(scale);
615         mNotificationsScrim.setScaleY(scale);
616     }
617 
getBackScaling()618     public float getBackScaling() {
619         return mNotificationsScrim.getScaleY();
620     }
621 
onTrackingStarted()622     public void onTrackingStarted() {
623         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
624         if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
625             mAnimatingPanelExpansionOnUnlock = false;
626         }
627     }
628 
629     @VisibleForTesting
onHideWallpaperTimeout()630     protected void onHideWallpaperTimeout() {
631         if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
632             return;
633         }
634 
635         holdWakeLock();
636         mWallpaperVisibilityTimedOut = true;
637         mAnimateChange = true;
638         mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration();
639         scheduleUpdate();
640     }
641 
holdWakeLock()642     private void holdWakeLock() {
643         if (!mWakeLockHeld) {
644             if (mWakeLock != null) {
645                 mWakeLockHeld = true;
646                 mWakeLock.acquire(TAG);
647             } else {
648                 Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
649             }
650         }
651     }
652 
653     /**
654      * Current state of the shade expansion when pulling it from the top.
655      * This value is 1 when on top of the keyguard and goes to 0 as the user drags up.
656      *
657      * The expansion fraction is tied to the scrim opacity.
658      *
659      * See {@link ScrimShadeTransitionController#onPanelExpansionChanged}.
660      *
661      * @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
662      */
setRawPanelExpansionFraction( @loatRangefrom = 0.0, to = 1.0) float rawPanelExpansionFraction)663     public void setRawPanelExpansionFraction(
664              @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
665         if (isNaN(rawPanelExpansionFraction)) {
666             throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN");
667         }
668         mRawPanelExpansionFraction = rawPanelExpansionFraction;
669         calculateAndUpdatePanelExpansion();
670     }
671 
672     /** See {@link ShadeViewController#setPanelScrimMinFraction(float)}. */
setPanelScrimMinFraction(float minFraction)673     public void setPanelScrimMinFraction(float minFraction) {
674         if (isNaN(minFraction)) {
675             throw new IllegalArgumentException("minFraction should not be NaN");
676         }
677         mPanelScrimMinFraction = minFraction;
678         calculateAndUpdatePanelExpansion();
679     }
680 
calculateAndUpdatePanelExpansion()681     private void calculateAndUpdatePanelExpansion() {
682         float panelExpansionFraction = mRawPanelExpansionFraction;
683         if (mPanelScrimMinFraction < 1.0f) {
684             panelExpansionFraction = Math.max(
685                     (mRawPanelExpansionFraction - mPanelScrimMinFraction)
686                             / (1.0f - mPanelScrimMinFraction),
687                     0);
688         }
689 
690         if (mPanelExpansionFraction != panelExpansionFraction) {
691             if (panelExpansionFraction != 0f
692                     && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
693                 mAnimatingPanelExpansionOnUnlock = true;
694             } else if (panelExpansionFraction == 0f) {
695                 mAnimatingPanelExpansionOnUnlock = false;
696             }
697 
698             mPanelExpansionFraction = panelExpansionFraction;
699 
700             boolean relevantState = (mState == ScrimState.UNLOCKED
701                     || mState == ScrimState.KEYGUARD
702                     || mState == ScrimState.DREAMING
703                     || mState == ScrimState.SHADE_LOCKED
704                     || mState == ScrimState.PULSING);
705             if (!(relevantState && mExpansionAffectsAlpha) || mAnimatingPanelExpansionOnUnlock) {
706                 return;
707             }
708             applyAndDispatchState();
709         }
710     }
711 
onUnlockAnimationFinished()712     public void onUnlockAnimationFinished() {
713         mAnimatingPanelExpansionOnUnlock = false;
714         applyAndDispatchState();
715     }
716 
717     /**
718      * Set the amount of progress we are currently in if we're transitioning to the full shade.
719      * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
720      * shade.
721      *
722      * @param progress the progress for all scrims.
723      * @param lockScreenNotificationsProgress the progress specifically for the notifications scrim.
724      */
setTransitionToFullShadeProgress(float progress, float lockScreenNotificationsProgress)725     public void setTransitionToFullShadeProgress(float progress,
726             float lockScreenNotificationsProgress) {
727         if (progress != mTransitionToFullShadeProgress || lockScreenNotificationsProgress
728                 != mTransitionToLockScreenFullShadeNotificationsProgress) {
729             mTransitionToFullShadeProgress = progress;
730             mTransitionToLockScreenFullShadeNotificationsProgress = lockScreenNotificationsProgress;
731             setTransitionToFullShade(progress > 0.0f || lockScreenNotificationsProgress > 0.0f);
732             applyAndDispatchState();
733         }
734     }
735 
736     /**
737      * Set if we're currently transitioning to the full shade
738      */
setTransitionToFullShade(boolean transitioning)739     private void setTransitionToFullShade(boolean transitioning) {
740         if (transitioning != mTransitioningToFullShade) {
741             mTransitioningToFullShade = transitioning;
742         }
743     }
744 
745 
746     /**
747      * Set bounds for notifications background, all coordinates are absolute
748      */
setNotificationsBounds(float left, float top, float right, float bottom)749     public void setNotificationsBounds(float left, float top, float right, float bottom) {
750         if (mClipsQsScrim) {
751             // notification scrim's rounded corners are anti-aliased, but clipping of the QS/behind
752             // scrim can't be and it's causing jagged corners. That's why notification scrim needs
753             // to overlap QS scrim by one pixel horizontally (left - 1 and right + 1)
754             // see: b/186644628
755             mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom);
756             mScrimBehind.setBottomEdgePosition((int) top);
757         } else {
758             mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
759         }
760     }
761 
762     /**
763      * Sets the amount of vertical over scroll that should be performed on the notifications scrim.
764      */
setNotificationsOverScrollAmount(int overScrollAmount)765     public void setNotificationsOverScrollAmount(int overScrollAmount) {
766         if (mNotificationsScrim != null) mNotificationsScrim.setTranslationY(overScrollAmount);
767     }
768 
769     /**
770      * Current state of the QuickSettings when pulling it from the top.
771      *
772      * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
773      * @param qsPanelBottomY Absolute Y position of qs panel bottom
774      */
setQsPosition(float expansionFraction, int qsPanelBottomY)775     public void setQsPosition(float expansionFraction, int qsPanelBottomY) {
776         if (isNaN(expansionFraction)) {
777             return;
778         }
779         expansionFraction = ShadeInterpolation.getNotificationScrimAlpha(expansionFraction);
780         boolean qsBottomVisible = qsPanelBottomY > 0;
781         if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) {
782             mQsExpansion = expansionFraction;
783             mQsBottomVisible = qsBottomVisible;
784             boolean relevantState = (mState == ScrimState.SHADE_LOCKED
785                     || mState == ScrimState.KEYGUARD
786                     || mState == ScrimState.PULSING);
787             if (!(relevantState && mExpansionAffectsAlpha)) {
788                 return;
789             }
790             applyAndDispatchState();
791         }
792     }
793 
794     /**
795      * Updates the percentage of the bouncer which is hidden.
796      */
setBouncerHiddenFraction(@loatRangefrom = 0, to = 1) float bouncerHiddenAmount)797     public void setBouncerHiddenFraction(@FloatRange(from = 0, to = 1) float bouncerHiddenAmount) {
798         if (mBouncerHiddenFraction == bouncerHiddenAmount) {
799             return;
800         }
801         mBouncerHiddenFraction = bouncerHiddenAmount;
802         if (mState == ScrimState.DREAMING) {
803             // Only the dreaming state requires this for the scrim calculation, so we should
804             // only trigger an update if dreaming.
805             applyAndDispatchState();
806         }
807     }
808 
809     /**
810      * If QS and notification scrims should not overlap, and should be clipped to each other's
811      * bounds instead.
812      */
setClipsQsScrim(boolean clipScrim)813     public void setClipsQsScrim(boolean clipScrim) {
814         if (clipScrim == mClipsQsScrim) {
815             return;
816         }
817         mClipsQsScrim = clipScrim;
818         for (ScrimState state : ScrimState.values()) {
819             state.setClipQsScrim(mClipsQsScrim);
820         }
821         if (mScrimBehind != null) {
822             mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim);
823         }
824         if (mState != ScrimState.UNINITIALIZED) {
825             // the clipScrimState has changed, let's reprepare ourselves
826             mState.prepare(mState);
827             applyAndDispatchState();
828         }
829     }
830 
831     @VisibleForTesting
getClipQsScrim()832     public boolean getClipQsScrim() {
833         return mClipsQsScrim;
834     }
835 
setOccludeAnimationPlaying(boolean occludeAnimationPlaying)836     public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) {
837         mOccludeAnimationPlaying = occludeAnimationPlaying;
838 
839         for (ScrimState state : ScrimState.values()) {
840             state.setOccludeAnimationPlaying(occludeAnimationPlaying);
841         }
842 
843         applyAndDispatchState();
844     }
845 
setOrAdaptCurrentAnimation(@ullable View scrim)846     private void setOrAdaptCurrentAnimation(@Nullable View scrim) {
847         if (scrim == null) {
848             return;
849         }
850 
851         float alpha = getCurrentScrimAlpha(scrim);
852         boolean qsScrimPullingDown = scrim == mScrimBehind && mQsBottomVisible;
853         if (isAnimating(scrim) && !qsScrimPullingDown) {
854             // Adapt current animation.
855             ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM);
856             float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA);
857             float previousStartValue = (Float) scrim.getTag(TAG_START_ALPHA);
858             float relativeDiff = alpha - previousEndValue;
859             float newStartValue = previousStartValue + relativeDiff;
860             scrim.setTag(TAG_START_ALPHA, newStartValue);
861             scrim.setTag(TAG_END_ALPHA, alpha);
862             previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
863         } else {
864             // Set animation.
865             updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
866         }
867     }
868 
applyState()869     private void applyState() {
870         mInFrontTint = mState.getFrontTint();
871         mBehindTint = mState.getBehindTint();
872         mNotificationsTint = mState.getNotifTint();
873 
874         mInFrontAlpha = mState.getFrontAlpha();
875         mBehindAlpha = mState.getBehindAlpha();
876         mNotificationsAlpha = mState.getNotifAlpha();
877 
878         assertAlphasValid();
879 
880         if (!mExpansionAffectsAlpha) {
881             return;
882         }
883 
884         if (mState == ScrimState.UNLOCKED || mState == ScrimState.DREAMING) {
885             final boolean occluding =
886                     mOccludeAnimationPlaying || mState.mLaunchingAffordanceWithPreview;
887             // Darken scrim as it's pulled down while unlocked. If we're unlocked but playing the
888             // screen off/occlusion animations, ignore expansion changes while those animations
889             // play.
890             if (!mScreenOffAnimationController.shouldExpandNotifications()
891                     && !mAnimatingPanelExpansionOnUnlock
892                     && !occluding) {
893                 if (mTransparentScrimBackground) {
894                     mBehindAlpha = 0;
895                     mNotificationsAlpha = 0;
896                 } else if (mClipsQsScrim) {
897                     float behindFraction = getInterpolatedFraction();
898                     behindFraction = (float) Math.pow(behindFraction, 0.8f);
899                     mBehindAlpha = 1;
900                     mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
901                 } else {
902                     mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha(
903                             mPanelExpansionFraction * mDefaultScrimAlpha);
904                     mNotificationsAlpha =
905                             mLargeScreenShadeInterpolator.getNotificationScrimAlpha(
906                                     mPanelExpansionFraction);
907                 }
908                 mBehindTint = mState.getBehindTint();
909                 mInFrontAlpha = 0;
910             }
911 
912             if (mState == ScrimState.DREAMING
913                     && mBouncerHiddenFraction != KeyguardBouncerConstants.EXPANSION_HIDDEN) {
914                 final float interpolatedFraction =
915                         BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
916                                 mBouncerHiddenFraction);
917                 mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, mBehindAlpha,
918                         interpolatedFraction);
919                 mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
920                         mBehindTint,
921                         interpolatedFraction);
922             }
923         } else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) {
924             mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f);
925         } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
926                 || mState == ScrimState.PULSING) {
927             Pair<Integer, Float> result = calculateBackStateForState(mState);
928             int behindTint = result.first;
929             float behindAlpha = result.second;
930             if (mTransitionToFullShadeProgress > 0.0f) {
931                 Pair<Integer, Float> shadeResult = calculateBackStateForState(
932                         ScrimState.SHADE_LOCKED);
933                 behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second,
934                         mTransitionToFullShadeProgress);
935                 behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first,
936                         mTransitionToFullShadeProgress);
937             }
938             mInFrontAlpha = mState.getFrontAlpha();
939             if (mClipsQsScrim) {
940                 mNotificationsAlpha = behindAlpha;
941                 mNotificationsTint = behindTint;
942                 mBehindAlpha = 1;
943                 mBehindTint = Color.BLACK;
944             } else {
945                 mBehindAlpha = behindAlpha;
946                 if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) {
947                     mNotificationsAlpha = MathUtils
948                             .saturate(mTransitionToLockScreenFullShadeNotificationsProgress);
949                 } else if (mState == ScrimState.SHADE_LOCKED) {
950                     // going from KEYGUARD to SHADE_LOCKED state
951                     mNotificationsAlpha = getInterpolatedFraction();
952                 } else {
953                     mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion);
954                 }
955                 mNotificationsTint = mState.getNotifTint();
956                 mBehindTint = behindTint;
957             }
958 
959             // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or
960             // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0
961             // otherwise there is a flicker to its previous value.
962             boolean hideNotificationScrim = (mState == ScrimState.KEYGUARD
963                     && mTransitionToFullShadeProgress == 0
964                     && mQsExpansion == 0
965                     && !mClipsQsScrim);
966             if (mKeyguardOccluded || hideNotificationScrim) {
967                 mNotificationsAlpha = 0;
968             }
969         }
970         if (mState != ScrimState.UNLOCKED) {
971             mAnimatingPanelExpansionOnUnlock = false;
972         }
973 
974         assertAlphasValid();
975     }
976 
assertAlphasValid()977     private void assertAlphasValid() {
978         if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
979             throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
980                     + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
981                     + mNotificationsAlpha);
982         }
983     }
984 
calculateBackStateForState(ScrimState state)985     private Pair<Integer, Float> calculateBackStateForState(ScrimState state) {
986         // Either darken of make the scrim transparent when you
987         // pull down the shade
988         float interpolatedFract = getInterpolatedFraction();
989 
990         float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
991         float behindAlpha;
992         int behindTint = state.getBehindTint();
993         if (mDarkenWhileDragging) {
994             behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
995                     interpolatedFract);
996         } else {
997             behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
998                     interpolatedFract);
999         }
1000         if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
1001             if (mClipsQsScrim) {
1002                 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
1003                     state.getNotifTint(), interpolatedFract);
1004             } else {
1005                 behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
1006                     state.getBehindTint(), interpolatedFract);
1007             }
1008         }
1009         if (mQsExpansion > 0) {
1010             behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
1011             float tintProgress = mQsExpansion;
1012             if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
1013                 // this is case of - on lockscreen - going from expanded QS to bouncer.
1014                 // Because mQsExpansion is already interpolated and transition between tints
1015                 // is too slow, we want to speed it up and make it more aligned to bouncer
1016                 // showing up progress. This issue is visible on large screens, both portrait and
1017                 // split shade because then transition is between very different tints
1018                 tintProgress = BouncerPanelExpansionCalculator
1019                         .showBouncerProgress(mPanelExpansionFraction);
1020             }
1021             int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
1022                     : ScrimState.SHADE_LOCKED.getBehindTint();
1023             behindTint = ColorUtils.blendARGB(behindTint, stateTint, tintProgress);
1024         }
1025 
1026         // If the keyguard is going away, we should not be opaque.
1027         if (mKeyguardStateController.isKeyguardGoingAway()) {
1028             behindAlpha = 0f;
1029         }
1030 
1031         return new Pair<>(behindTint, behindAlpha);
1032     }
1033 
1034 
applyAndDispatchState()1035     private void applyAndDispatchState() {
1036         applyState();
1037         if (mUpdatePending) {
1038             return;
1039         }
1040         setOrAdaptCurrentAnimation(mScrimBehind);
1041         setOrAdaptCurrentAnimation(mNotificationsScrim);
1042         setOrAdaptCurrentAnimation(mScrimInFront);
1043         dispatchBackScrimState(mScrimBehind.getViewAlpha());
1044 
1045         // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
1046         // and docking.
1047         if (mWallpaperVisibilityTimedOut) {
1048             mWallpaperVisibilityTimedOut = false;
1049             DejankUtils.postAfterTraversal(() -> {
1050                 mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
1051                         AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
1052             });
1053         }
1054     }
1055 
1056     /**
1057      * Sets the front scrim opacity in AOD so it's not as bright.
1058      * <p>
1059      * Displays usually don't support multiple dimming settings when in low power mode.
1060      * The workaround is to modify the front scrim opacity when in AOD, so it's not as
1061      * bright when you're at the movies or lying down on bed.
1062      * <p>
1063      * This value will be lost during transitions and only updated again after the the
1064      * device is dozing when the light sensor is on.
1065      */
setAodFrontScrimAlpha(float alpha)1066     public void setAodFrontScrimAlpha(float alpha) {
1067         if (mInFrontAlpha != alpha && shouldUpdateFrontScrimAlpha()) {
1068             mInFrontAlpha = alpha;
1069             updateScrims();
1070         }
1071 
1072         mState.AOD.setAodFrontScrimAlpha(alpha);
1073         mState.PULSING.setAodFrontScrimAlpha(alpha);
1074     }
1075 
shouldUpdateFrontScrimAlpha()1076     private boolean shouldUpdateFrontScrimAlpha() {
1077         if (mState == ScrimState.AOD
1078                 && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) {
1079             return true;
1080         }
1081 
1082         if (mState == ScrimState.PULSING) {
1083             return true;
1084         }
1085 
1086         return false;
1087     }
1088 
1089     /**
1090      * If the lock screen sensor is active.
1091      */
setWakeLockScreenSensorActive(boolean active)1092     public void setWakeLockScreenSensorActive(boolean active) {
1093         for (ScrimState state : ScrimState.values()) {
1094             state.setWakeLockScreenSensorActive(active);
1095         }
1096 
1097         if (mState == ScrimState.PULSING) {
1098             float newBehindAlpha = mState.getBehindAlpha();
1099             if (mBehindAlpha != newBehindAlpha) {
1100                 mBehindAlpha = newBehindAlpha;
1101                 if (isNaN(mBehindAlpha)) {
1102                     throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
1103                             + ", back: " + mBehindAlpha);
1104                 }
1105                 updateScrims();
1106             }
1107         }
1108     }
1109 
scheduleUpdate()1110     protected void scheduleUpdate() {
1111         if (mUpdatePending || mScrimBehind == null) return;
1112 
1113         // Make sure that a frame gets scheduled.
1114         mScrimBehind.invalidate();
1115         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
1116         mUpdatePending = true;
1117     }
1118 
updateScrims()1119     protected void updateScrims() {
1120         // Make sure we have the right gradients and their opacities will satisfy GAR.
1121         if (mNeedsDrawableColorUpdate) {
1122             mNeedsDrawableColorUpdate = false;
1123             // Only animate scrim color if the scrim view is actually visible
1124             boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
1125             boolean animateBehindScrim = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
1126             boolean animateScrimNotifications = mNotificationsScrim.getViewAlpha() != 0
1127                     && !mBlankScreen;
1128 
1129             mScrimInFront.setColors(mColors, animateScrimInFront);
1130             mScrimBehind.setColors(mColors, animateBehindScrim);
1131             mNotificationsScrim.setColors(mColors, animateScrimNotifications);
1132 
1133             dispatchBackScrimState(mScrimBehind.getViewAlpha());
1134         }
1135 
1136         // We want to override the back scrim opacity for the AOD state
1137         // when it's time to fade the wallpaper away.
1138         boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING)
1139                 && mWallpaperVisibilityTimedOut;
1140         // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
1141         boolean hideFlagShowWhenLockedActivities =
1142                 (mState == ScrimState.PULSING || mState == ScrimState.AOD)
1143                 && mKeyguardOccluded;
1144         if (aodWallpaperTimeout || hideFlagShowWhenLockedActivities) {
1145             mBehindAlpha = 1;
1146         }
1147         // Prevent notification scrim flicker when transitioning away from keyguard.
1148         if (mKeyguardStateController.isKeyguardGoingAway()) {
1149             mNotificationsAlpha = 0;
1150         }
1151 
1152         // Prevent flickering for activities above keyguard and quick settings in keyguard.
1153         if (mKeyguardOccluded
1154                 && (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED)) {
1155             mBehindAlpha = 0;
1156             mNotificationsAlpha = 0;
1157         }
1158 
1159         setScrimAlpha(mScrimInFront, mInFrontAlpha);
1160         setScrimAlpha(mScrimBehind, mBehindAlpha);
1161         setScrimAlpha(mNotificationsScrim, mNotificationsAlpha);
1162 
1163         // The animation could have all already finished, let's call onFinished just in case
1164         onFinished(mState);
1165         dispatchScrimsVisible();
1166     }
1167 
dispatchBackScrimState(float alpha)1168     private void dispatchBackScrimState(float alpha) {
1169         // When clipping QS, the notification scrim is the one that feels behind.
1170         // mScrimBehind will be drawing black and its opacity will always be 1.
1171         if (mClipsQsScrim && mQsBottomVisible) {
1172             alpha = mNotificationsAlpha;
1173         }
1174         mScrimStateListener.accept(mState, alpha, mColors);
1175     }
1176 
dispatchScrimsVisible()1177     private void dispatchScrimsVisible() {
1178         final ScrimView backScrim = mClipsQsScrim ? mNotificationsScrim : mScrimBehind;
1179         final int currentScrimVisibility;
1180         if (mScrimInFront.getViewAlpha() == 1 || backScrim.getViewAlpha() == 1) {
1181             currentScrimVisibility = OPAQUE;
1182         } else if (mScrimInFront.getViewAlpha() == 0 && backScrim.getViewAlpha() == 0) {
1183             currentScrimVisibility = TRANSPARENT;
1184         } else {
1185             currentScrimVisibility = SEMI_TRANSPARENT;
1186         }
1187 
1188         if (mScrimsVisibility != currentScrimVisibility) {
1189             mScrimsVisibility = currentScrimVisibility;
1190             mScrimVisibleListener.accept(currentScrimVisibility);
1191         }
1192     }
1193 
getInterpolatedFraction()1194     private float getInterpolatedFraction() {
1195         if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
1196             return BouncerPanelExpansionCalculator
1197                     .aboutToShowBouncerProgress(mPanelExpansionFraction);
1198         }
1199         return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction);
1200     }
1201 
setScrimAlpha(ScrimView scrim, float alpha)1202     private void setScrimAlpha(ScrimView scrim, float alpha) {
1203         if (alpha == 0f) {
1204             scrim.setClickable(false);
1205         } else {
1206             // Eat touch events (unless dozing).
1207             scrim.setClickable(mState != ScrimState.AOD);
1208         }
1209         updateScrim(scrim, alpha);
1210     }
1211 
getScrimName(ScrimView scrim)1212     private String getScrimName(ScrimView scrim) {
1213         if (scrim == mScrimInFront) {
1214             return "front_scrim";
1215         } else if (scrim == mScrimBehind) {
1216             return "behind_scrim";
1217         } else if (scrim == mNotificationsScrim) {
1218             return "notifications_scrim";
1219         }
1220         return "unknown_scrim";
1221     }
1222 
updateScrimColor(View scrim, float alpha, int tint)1223     private void updateScrimColor(View scrim, float alpha, int tint) {
1224         alpha = Math.max(0, Math.min(1.0f, alpha));
1225         if (scrim instanceof ScrimView) {
1226             ScrimView scrimView = (ScrimView) scrim;
1227             if (DEBUG_MODE) {
1228                 tint = getDebugScrimTint(scrimView);
1229             }
1230 
1231             Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_alpha",
1232                     (int) (alpha * 255));
1233 
1234             Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
1235                     Color.alpha(tint));
1236             scrimView.setTint(tint);
1237             if (!mIsBouncerToGoneTransitionRunning) {
1238                 scrimView.setViewAlpha(alpha);
1239             }
1240         } else {
1241             scrim.setAlpha(alpha);
1242         }
1243         dispatchScrimsVisible();
1244     }
1245 
getDebugScrimTint(ScrimView scrim)1246     private int getDebugScrimTint(ScrimView scrim) {
1247         if (scrim == mScrimBehind) return DEBUG_BEHIND_TINT;
1248         if (scrim == mScrimInFront) return DEBUG_FRONT_TINT;
1249         if (scrim == mNotificationsScrim) return DEBUG_NOTIFICATIONS_TINT;
1250         throw new RuntimeException("scrim can't be matched with known scrims");
1251     }
1252 
startScrimAnimation(final View scrim, float current)1253     private void startScrimAnimation(final View scrim, float current) {
1254         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1255         if (mAnimatorListener != null) {
1256             anim.addListener(mAnimatorListener);
1257         }
1258         final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() :
1259                 Color.TRANSPARENT;
1260         anim.addUpdateListener(animation -> {
1261             final float startAlpha = (Float) scrim.getTag(TAG_START_ALPHA);
1262             final float animAmount = (float) animation.getAnimatedValue();
1263             final int finalScrimTint = getCurrentScrimTint(scrim);
1264             final float finalScrimAlpha = getCurrentScrimAlpha(scrim);
1265             float alpha = MathUtils.lerp(startAlpha, finalScrimAlpha, animAmount);
1266             alpha = MathUtils.constrain(alpha, 0f, 1f);
1267             int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount);
1268             updateScrimColor(scrim, alpha, tint);
1269             dispatchScrimsVisible();
1270         });
1271         anim.setInterpolator(mInterpolator);
1272         anim.setStartDelay(mAnimationDelay);
1273         anim.setDuration(mAnimationDuration);
1274         anim.addListener(new AnimatorListenerAdapter() {
1275             private final ScrimState mLastState = mState;
1276             private final Callback mLastCallback = mCallback;
1277 
1278             @Override
1279             public void onAnimationEnd(Animator animation) {
1280                 scrim.setTag(TAG_KEY_ANIM, null);
1281                 onFinished(mLastCallback, mLastState);
1282 
1283                 dispatchScrimsVisible();
1284             }
1285         });
1286 
1287         // Cache alpha values because we might want to update this animator in the future if
1288         // the user expands the panel while the animation is still running.
1289         scrim.setTag(TAG_START_ALPHA, current);
1290         scrim.setTag(TAG_END_ALPHA, getCurrentScrimAlpha(scrim));
1291 
1292         scrim.setTag(TAG_KEY_ANIM, anim);
1293         anim.start();
1294     }
1295 
getCurrentScrimAlpha(View scrim)1296     private float getCurrentScrimAlpha(View scrim) {
1297         if (scrim == mScrimInFront) {
1298             return mInFrontAlpha;
1299         } else if (scrim == mScrimBehind) {
1300             return mBehindAlpha;
1301         } else if (scrim == mNotificationsScrim) {
1302             return mNotificationsAlpha;
1303         } else {
1304             throw new IllegalArgumentException("Unknown scrim view");
1305         }
1306     }
1307 
getCurrentScrimTint(View scrim)1308     private int getCurrentScrimTint(View scrim) {
1309         if (scrim == mScrimInFront) {
1310             return mInFrontTint;
1311         } else if (scrim == mScrimBehind) {
1312             return mBehindTint;
1313         } else if (scrim == mNotificationsScrim) {
1314             return mNotificationsTint;
1315         } else {
1316             throw new IllegalArgumentException("Unknown scrim view");
1317         }
1318     }
1319 
1320     @Override
onPreDraw()1321     public boolean onPreDraw() {
1322         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
1323         mUpdatePending = false;
1324         if (mCallback != null) {
1325             mCallback.onStart();
1326         }
1327         updateScrims();
1328         return true;
1329     }
1330 
1331     /**
1332      * @param state that finished
1333      */
onFinished(ScrimState state)1334     private void onFinished(ScrimState state) {
1335         onFinished(mCallback, state);
1336     }
1337 
onFinished(Callback callback, ScrimState state)1338     private void onFinished(Callback callback, ScrimState state) {
1339         if (mPendingFrameCallback != null) {
1340             // No animations can finish while we're waiting on the blanking to finish
1341             return;
1342 
1343         }
1344         if (isAnimating(mScrimBehind)
1345                 || isAnimating(mNotificationsScrim)
1346                 || isAnimating(mScrimInFront)) {
1347             if (callback != null && callback != mCallback) {
1348                 // Since we only notify the callback that we're finished once everything has
1349                 // finished, we need to make sure that any changing callbacks are also invoked
1350                 callback.onFinished();
1351             }
1352             return;
1353         }
1354         if (mWakeLockHeld) {
1355             mWakeLock.release(TAG);
1356             mWakeLockHeld = false;
1357         }
1358 
1359         if (callback != null) {
1360             callback.onFinished();
1361 
1362             if (callback == mCallback) {
1363                 mCallback = null;
1364             }
1365         }
1366 
1367         // When unlocking with fingerprint, we'll fade the scrims from black to transparent.
1368         // At the end of the animation we need to remove the tint.
1369         if (state == ScrimState.UNLOCKED) {
1370             mInFrontTint = Color.TRANSPARENT;
1371             mBehindTint = mState.getBehindTint();
1372             mNotificationsTint = mState.getNotifTint();
1373             updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
1374             updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
1375             updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint);
1376         }
1377     }
1378 
isAnimating(@ullable View scrim)1379     private boolean isAnimating(@Nullable View scrim) {
1380         return scrim != null && scrim.getTag(TAG_KEY_ANIM) != null;
1381     }
1382 
1383     @VisibleForTesting
setAnimatorListener(Animator.AnimatorListener animatorListener)1384     void setAnimatorListener(Animator.AnimatorListener animatorListener) {
1385         mAnimatorListener = animatorListener;
1386     }
1387 
updateScrim(ScrimView scrim, float alpha)1388     private void updateScrim(ScrimView scrim, float alpha) {
1389         final float currentAlpha = scrim.getViewAlpha();
1390 
1391         ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM);
1392         if (previousAnimator != null) {
1393             // Previous animators should always be cancelled. Not doing so would cause
1394             // overlap, especially on states that don't animate, leading to flickering,
1395             // and in the worst case, an internal state that doesn't represent what
1396             // transitionTo requested.
1397             cancelAnimator(previousAnimator);
1398         }
1399 
1400         if (mPendingFrameCallback != null) {
1401             // Display is off and we're waiting.
1402             return;
1403         } else if (mBlankScreen) {
1404             // Need to blank the display before continuing.
1405             blankDisplay();
1406             return;
1407         } else if (!mScreenBlankingCallbackCalled) {
1408             // Not blanking the screen. Letting the callback know that we're ready
1409             // to replace what was on the screen before.
1410             if (mCallback != null) {
1411                 mCallback.onDisplayBlanked();
1412                 mScreenBlankingCallbackCalled = true;
1413             }
1414         }
1415 
1416         if (scrim == mScrimBehind) {
1417             dispatchBackScrimState(alpha);
1418         }
1419 
1420         final boolean wantsAlphaUpdate = alpha != currentAlpha;
1421         final boolean wantsTintUpdate = scrim.getTint() != getCurrentScrimTint(scrim);
1422 
1423         if (wantsAlphaUpdate || wantsTintUpdate) {
1424             if (mAnimateChange) {
1425                 startScrimAnimation(scrim, currentAlpha);
1426             } else {
1427                 // update the alpha directly
1428                 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
1429             }
1430         }
1431     }
1432 
cancelAnimator(ValueAnimator previousAnimator)1433     private void cancelAnimator(ValueAnimator previousAnimator) {
1434         if (previousAnimator != null) {
1435             previousAnimator.cancel();
1436         }
1437     }
1438 
blankDisplay()1439     private void blankDisplay() {
1440         updateScrimColor(mScrimInFront, 1, Color.BLACK);
1441 
1442         // Notify callback that the screen is completely black and we're
1443         // ready to change the display power mode
1444         mPendingFrameCallback = () -> {
1445             if (mCallback != null) {
1446                 mCallback.onDisplayBlanked();
1447                 mScreenBlankingCallbackCalled = true;
1448             }
1449 
1450             mBlankingTransitionRunnable = () -> {
1451                 mBlankingTransitionRunnable = null;
1452                 mPendingFrameCallback = null;
1453                 mBlankScreen = false;
1454                 // Try again.
1455                 updateScrims();
1456             };
1457 
1458             // Setting power states can happen after we push out the frame. Make sure we
1459             // stay fully opaque until the power state request reaches the lower levels.
1460             final int delay = mScreenOn ? 32 : 500;
1461             if (DEBUG) {
1462                 Log.d(TAG, "Fading out scrims with delay: " + delay);
1463             }
1464             mHandler.postDelayed(mBlankingTransitionRunnable, delay);
1465         };
1466         doOnTheNextFrame(mPendingFrameCallback);
1467     }
1468 
1469     /**
1470      * Executes a callback after the frame has hit the display.
1471      *
1472      * @param callback What to run.
1473      */
1474     @VisibleForTesting
doOnTheNextFrame(Runnable callback)1475     protected void doOnTheNextFrame(Runnable callback) {
1476         // Just calling View#postOnAnimation isn't enough because the frame might not have reached
1477         // the display yet. A timeout is the safest solution.
1478         mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
1479     }
1480 
setScrimBehindChangeRunnable(Runnable changeRunnable)1481     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
1482         // TODO: remove this. This is necessary because of an order-of-operations limitation.
1483         // The fix is to move more of these class into @CentralSurfacesScope
1484         if (mScrimBehind == null) {
1485             mScrimBehindChangeRunnable = changeRunnable;
1486         } else {
1487             mScrimBehind.setChangeRunnable(changeRunnable, mMainExecutor);
1488         }
1489     }
1490 
updateThemeColors()1491     private void updateThemeColors() {
1492         if (mScrimBehind == null) return;
1493         int background = Utils.getColorAttr(mScrimBehind.getContext(),
1494                 android.R.attr.colorBackgroundFloating).getDefaultColor();
1495         int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor();
1496         mColors.setMainColor(background);
1497         mColors.setSecondaryColor(accent);
1498         final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
1499         mColors.setSupportsDarkText(isBackgroundLight);
1500 
1501         int surface = Utils.getColorAttr(mScrimBehind.getContext(),
1502                 com.android.internal.R.attr.materialColorSurface).getDefaultColor();
1503         for (ScrimState state : ScrimState.values()) {
1504             state.setSurfaceColor(surface);
1505         }
1506 
1507         mNeedsDrawableColorUpdate = true;
1508     }
1509 
onThemeChanged()1510     private void onThemeChanged() {
1511         updateThemeColors();
1512         scheduleUpdate();
1513     }
1514 
1515     @Override
dump(PrintWriter pw, String[] args)1516     public void dump(PrintWriter pw, String[] args) {
1517         pw.println(" ScrimController: ");
1518         pw.print("  state: ");
1519         pw.println(mState);
1520         pw.println("    mClipQsScrim = " + mState.mClipQsScrim);
1521 
1522         pw.print("  frontScrim:");
1523         pw.print(" viewAlpha=");
1524         pw.print(mScrimInFront.getViewAlpha());
1525         pw.print(" alpha=");
1526         pw.print(mInFrontAlpha);
1527         pw.print(" tint=0x");
1528         pw.println(Integer.toHexString(mScrimInFront.getTint()));
1529 
1530         pw.print("  behindScrim:");
1531         pw.print(" viewAlpha=");
1532         pw.print(mScrimBehind.getViewAlpha());
1533         pw.print(" alpha=");
1534         pw.print(mBehindAlpha);
1535         pw.print(" tint=0x");
1536         pw.println(Integer.toHexString(mScrimBehind.getTint()));
1537 
1538         pw.print("  notificationsScrim:");
1539         pw.print(" viewAlpha=");
1540         pw.print(mNotificationsScrim.getViewAlpha());
1541         pw.print(" alpha=");
1542         pw.print(mNotificationsAlpha);
1543         pw.print(" tint=0x");
1544         pw.println(Integer.toHexString(mNotificationsScrim.getTint()));
1545         pw.print(" expansionProgress=");
1546         pw.println(mTransitionToLockScreenFullShadeNotificationsProgress);
1547 
1548         pw.print("  mDefaultScrimAlpha=");
1549         pw.println(mDefaultScrimAlpha);
1550         pw.print("  mPanelExpansionFraction=");
1551         pw.println(mPanelExpansionFraction);
1552         pw.print("  mExpansionAffectsAlpha=");
1553         pw.println(mExpansionAffectsAlpha);
1554 
1555         pw.print("  mState.getMaxLightRevealScrimAlpha=");
1556         pw.println(mState.getMaxLightRevealScrimAlpha());
1557     }
1558 
setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode)1559     private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
1560         mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
1561         ScrimState[] states = ScrimState.values();
1562         for (int i = 0; i < states.length; i++) {
1563             states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode);
1564         }
1565     }
1566 
1567     /**
1568      * Interrupts blanking transitions once the display notifies that it's already on.
1569      */
onScreenTurnedOn()1570     public void onScreenTurnedOn() {
1571         mScreenOn = true;
1572         if (mHandler.hasCallbacks(mBlankingTransitionRunnable)) {
1573             if (DEBUG) {
1574                 Log.d(TAG, "Shorter blanking because screen turned on. All good.");
1575             }
1576             mHandler.removeCallbacks(mBlankingTransitionRunnable);
1577             mBlankingTransitionRunnable.run();
1578         }
1579     }
1580 
onScreenTurnedOff()1581     public void onScreenTurnedOff() {
1582         mScreenOn = false;
1583     }
1584 
setExpansionAffectsAlpha(boolean expansionAffectsAlpha)1585     public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
1586         mExpansionAffectsAlpha = expansionAffectsAlpha;
1587     }
1588 
setKeyguardOccluded(boolean keyguardOccluded)1589     public void setKeyguardOccluded(boolean keyguardOccluded) {
1590         if (mKeyguardOccluded == keyguardOccluded) {
1591             return;
1592         }
1593         mKeyguardOccluded = keyguardOccluded;
1594         updateScrims();
1595     }
1596 
setHasBackdrop(boolean hasBackdrop)1597     public void setHasBackdrop(boolean hasBackdrop) {
1598         for (ScrimState state : ScrimState.values()) {
1599             state.setHasBackdrop(hasBackdrop);
1600         }
1601 
1602         // Backdrop event may arrive after state was already applied,
1603         // in this case, back-scrim needs to be re-evaluated
1604         if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
1605             float newBehindAlpha = mState.getBehindAlpha();
1606             if (isNaN(newBehindAlpha)) {
1607                 throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
1608                         + ", back: " + mBehindAlpha);
1609             }
1610             if (mBehindAlpha != newBehindAlpha) {
1611                 mBehindAlpha = newBehindAlpha;
1612                 updateScrims();
1613             }
1614         }
1615     }
1616 
setKeyguardFadingAway(boolean fadingAway, long duration)1617     private void setKeyguardFadingAway(boolean fadingAway, long duration) {
1618         for (ScrimState state : ScrimState.values()) {
1619             state.setKeyguardFadingAway(fadingAway, duration);
1620         }
1621     }
1622 
setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview)1623     public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
1624         for (ScrimState state : ScrimState.values()) {
1625             state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
1626         }
1627     }
1628 
1629     public interface Callback {
onStart()1630         default void onStart() {
1631         }
1632 
onDisplayBlanked()1633         default void onDisplayBlanked() {
1634         }
1635 
onFinished()1636         default void onFinished() {
1637         }
1638 
onCancelled()1639         default void onCancelled() {
1640         }
1641     }
1642 
1643     /**
1644      * Simple keyguard callback that updates scrims when keyguard visibility changes.
1645      */
1646     private class KeyguardVisibilityCallback extends KeyguardUpdateMonitorCallback {
1647 
1648         @Override
onKeyguardVisibilityChanged(boolean visible)1649         public void onKeyguardVisibilityChanged(boolean visible) {
1650             mNeedsDrawableColorUpdate = true;
1651             scheduleUpdate();
1652         }
1653     }
1654 }
1655