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