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 android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.PropertyValuesHolder;
22 import android.animation.ValueAnimator;
23 import android.content.Context;
24 import android.graphics.Rect;
25 import android.support.v4.graphics.ColorUtils;
26 import android.util.TypedValue;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.ViewTreeObserver;
30 import android.view.animation.DecelerateInterpolator;
31 import android.view.animation.Interpolator;
32 import android.view.animation.PathInterpolator;
33 import com.android.keyguard.KeyguardUpdateMonitor;
34 import com.android.systemui.R;
35 import com.android.systemui.statusbar.ExpandableNotificationRow;
36 import com.android.systemui.statusbar.NotificationData;
37 import com.android.systemui.statusbar.ScrimView;
38 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
39 import com.android.systemui.statusbar.stack.ViewState;
40 
41 /**
42  * Controls both the scrim behind the notifications and in front of the notifications (when a
43  * security method gets shown).
44  */
45 public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
46         OnHeadsUpChangedListener {
47     public static final long ANIMATION_DURATION = 220;
48     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
49             = new PathInterpolator(0f, 0, 0.7f, 1f);
50     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
51             = new PathInterpolator(0.3f, 0f, 0.8f, 1f);
52     protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
53     protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
54     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
55     private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = 0.85f;
56     private static final int TAG_KEY_ANIM = R.id.scrim;
57     private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
58     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
59     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
60     private static final float NOT_INITIALIZED = -1;
61 
62     private final LightBarController mLightBarController;
63     protected final ScrimView mScrimBehind;
64     private final ScrimView mScrimInFront;
65     private final UnlockMethodCache mUnlockMethodCache;
66     private final View mHeadsUpScrim;
67     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
68 
69     protected float mScrimBehindAlpha;
70     protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
71     protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
72 
73     protected boolean mKeyguardShowing;
74     private float mFraction;
75 
76     private boolean mDarkenWhileDragging;
77     protected boolean mBouncerShowing;
78     protected boolean mBouncerIsKeyguard = false;
79     private boolean mWakeAndUnlocking;
80     protected boolean mAnimateChange;
81     private boolean mUpdatePending;
82     private boolean mTracking;
83     private boolean mAnimateKeyguardFadingOut;
84     protected long mDurationOverride = -1;
85     private long mAnimationDelay;
86     private Runnable mOnAnimationFinished;
87     private final Interpolator mInterpolator = new DecelerateInterpolator();
88     private boolean mDozing;
89     private float mDozeInFrontAlpha;
90     private float mDozeBehindAlpha;
91     private float mCurrentInFrontAlpha  = NOT_INITIALIZED;
92     private float mCurrentBehindAlpha = NOT_INITIALIZED;
93     private float mCurrentHeadsUpAlpha = NOT_INITIALIZED;
94     private int mPinnedHeadsUpCount;
95     private float mTopHeadsUpDragAmount;
96     private View mDraggedHeadsUpView;
97     private boolean mForceHideScrims;
98     private boolean mSkipFirstFrame;
99     private boolean mDontAnimateBouncerChanges;
100     private boolean mKeyguardFadingOutInProgress;
101     private ValueAnimator mKeyguardFadeoutAnimation;
102 
ScrimController(LightBarController lightBarController, ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim)103     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
104             ScrimView scrimInFront, View headsUpScrim) {
105         mScrimBehind = scrimBehind;
106         mScrimInFront = scrimInFront;
107         mHeadsUpScrim = headsUpScrim;
108         final Context context = scrimBehind.getContext();
109         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
110         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
111         mLightBarController = lightBarController;
112         mScrimBehindAlpha = context.getResources().getFloat(R.dimen.scrim_behind_alpha);
113 
114         updateHeadsUpScrim(false);
115         updateScrims();
116     }
117 
setKeyguardShowing(boolean showing)118     public void setKeyguardShowing(boolean showing) {
119         mKeyguardShowing = showing;
120         scheduleUpdate();
121     }
122 
setScrimBehindValues(float scrimBehindAlphaKeyguard, float scrimBehindAlphaUnlocking)123     protected void setScrimBehindValues(float scrimBehindAlphaKeyguard,
124             float scrimBehindAlphaUnlocking) {
125         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
126         mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking;
127         scheduleUpdate();
128     }
129 
onTrackingStarted()130     public void onTrackingStarted() {
131         mTracking = true;
132         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
133     }
134 
onExpandingFinished()135     public void onExpandingFinished() {
136         mTracking = false;
137     }
138 
setPanelExpansion(float fraction)139     public void setPanelExpansion(float fraction) {
140         if (mFraction != fraction) {
141             mFraction = fraction;
142             scheduleUpdate();
143             if (mPinnedHeadsUpCount != 0) {
144                 updateHeadsUpScrim(false);
145             }
146             if (mKeyguardFadeoutAnimation != null && mTracking) {
147                 mKeyguardFadeoutAnimation.cancel();
148             }
149         }
150     }
151 
setBouncerShowing(boolean showing)152     public void setBouncerShowing(boolean showing) {
153         mBouncerShowing = showing;
154         mAnimateChange = !mTracking && !mDontAnimateBouncerChanges;
155         scheduleUpdate();
156     }
157 
setWakeAndUnlocking()158     public void setWakeAndUnlocking() {
159         mWakeAndUnlocking = true;
160         scheduleUpdate();
161     }
162 
animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished, boolean skipFirstFrame)163     public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished,
164             boolean skipFirstFrame) {
165         mWakeAndUnlocking = false;
166         mAnimateKeyguardFadingOut = true;
167         mDurationOverride = duration;
168         mAnimationDelay = delay;
169         mAnimateChange = true;
170         mSkipFirstFrame = skipFirstFrame;
171         mOnAnimationFinished = onAnimationFinished;
172 
173         if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
174             scheduleUpdate();
175 
176             // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
177             // the changes we just scheduled.
178             onPreDraw();
179         } else {
180 
181             // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
182             // with too many things in this case, in order to not skip the initial frames.
183             mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
184         }
185     }
186 
abortKeyguardFadingOut()187     public void abortKeyguardFadingOut() {
188         if (mAnimateKeyguardFadingOut) {
189             endAnimateKeyguardFadingOut(true /* force */);
190         }
191     }
192 
animateKeyguardUnoccluding(long duration)193     public void animateKeyguardUnoccluding(long duration) {
194         mAnimateChange = false;
195         setScrimBehindColor(0f);
196         mAnimateChange = true;
197         scheduleUpdate();
198         mDurationOverride = duration;
199     }
200 
animateGoingToFullShade(long delay, long duration)201     public void animateGoingToFullShade(long delay, long duration) {
202         mDurationOverride = duration;
203         mAnimationDelay = delay;
204         mAnimateChange = true;
205         scheduleUpdate();
206     }
207 
setDozing(boolean dozing)208     public void setDozing(boolean dozing) {
209         if (mDozing != dozing) {
210             mDozing = dozing;
211             scheduleUpdate();
212         }
213     }
214 
setDozeInFrontAlpha(float alpha)215     public void setDozeInFrontAlpha(float alpha) {
216         mDozeInFrontAlpha = alpha;
217         updateScrimColor(mScrimInFront);
218     }
219 
setDozeBehindAlpha(float alpha)220     public void setDozeBehindAlpha(float alpha) {
221         mDozeBehindAlpha = alpha;
222         updateScrimColor(mScrimBehind);
223     }
224 
getDozeBehindAlpha()225     public float getDozeBehindAlpha() {
226         return mDozeBehindAlpha;
227     }
228 
getDozeInFrontAlpha()229     public float getDozeInFrontAlpha() {
230         return mDozeInFrontAlpha;
231     }
232 
getScrimInFrontAlpha()233     private float getScrimInFrontAlpha() {
234         return mKeyguardUpdateMonitor.needsSlowUnlockTransition()
235                 ? SCRIM_IN_FRONT_ALPHA_LOCKED
236                 : SCRIM_IN_FRONT_ALPHA;
237     }
238 
scheduleUpdate()239     protected void scheduleUpdate() {
240         if (mUpdatePending) return;
241 
242         // Make sure that a frame gets scheduled.
243         mScrimBehind.invalidate();
244         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
245         mUpdatePending = true;
246     }
247 
updateScrims()248     protected void updateScrims() {
249         if (mAnimateKeyguardFadingOut || mForceHideScrims) {
250             setScrimInFrontColor(0f);
251             setScrimBehindColor(0f);
252         } else if (mWakeAndUnlocking) {
253 
254             // During wake and unlock, we first hide everything behind a black scrim, which then
255             // gets faded out from animateKeyguardFadingOut.
256             if (mDozing) {
257                 setScrimInFrontColor(0f);
258                 setScrimBehindColor(1f);
259             } else {
260                 setScrimInFrontColor(1f);
261                 setScrimBehindColor(0f);
262             }
263         } else if (!mKeyguardShowing && !mBouncerShowing) {
264             updateScrimNormal();
265             setScrimInFrontColor(0);
266         } else {
267             updateScrimKeyguard();
268         }
269         mAnimateChange = false;
270     }
271 
updateScrimKeyguard()272     private void updateScrimKeyguard() {
273         if (mTracking && mDarkenWhileDragging) {
274             float behindFraction = Math.max(0, Math.min(mFraction, 1));
275             float fraction = 1 - behindFraction;
276             fraction = (float) Math.pow(fraction, 0.8f);
277             behindFraction = (float) Math.pow(behindFraction, 0.8f);
278             setScrimInFrontColor(fraction * getScrimInFrontAlpha());
279             setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard);
280         } else if (mBouncerShowing && !mBouncerIsKeyguard) {
281             setScrimInFrontColor(getScrimInFrontAlpha());
282             updateScrimNormal();
283         } else if (mBouncerShowing) {
284             setScrimInFrontColor(0f);
285             setScrimBehindColor(mScrimBehindAlpha);
286         } else {
287             float fraction = Math.max(0, Math.min(mFraction, 1));
288             setScrimInFrontColor(0f);
289             setScrimBehindColor(fraction
290                     * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
291                     + mScrimBehindAlphaUnlocking);
292         }
293     }
294 
updateScrimNormal()295     private void updateScrimNormal() {
296         float frac = mFraction;
297         // let's start this 20% of the way down the screen
298         frac = frac * 1.2f - 0.2f;
299         if (frac <= 0) {
300             setScrimBehindColor(0);
301         } else {
302             // woo, special effects
303             final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
304             setScrimBehindColor(k * mScrimBehindAlpha);
305         }
306     }
307 
setScrimBehindColor(float alpha)308     private void setScrimBehindColor(float alpha) {
309         setScrimColor(mScrimBehind, alpha);
310     }
311 
setScrimInFrontColor(float alpha)312     private void setScrimInFrontColor(float alpha) {
313         setScrimColor(mScrimInFront, alpha);
314         if (alpha == 0f) {
315             mScrimInFront.setClickable(false);
316         } else {
317 
318             // Eat touch events (unless dozing).
319             mScrimInFront.setClickable(!mDozing);
320         }
321     }
322 
setScrimColor(View scrim, float alpha)323     private void setScrimColor(View scrim, float alpha) {
324         updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim));
325     }
326 
getDozeAlpha(View scrim)327     protected float getDozeAlpha(View scrim) {
328         return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha;
329     }
330 
getCurrentScrimAlpha(View scrim)331     protected float getCurrentScrimAlpha(View scrim) {
332         return scrim == mScrimBehind ? mCurrentBehindAlpha
333                 : scrim == mScrimInFront ? mCurrentInFrontAlpha
334                 : mCurrentHeadsUpAlpha;
335     }
336 
setCurrentScrimAlpha(View scrim, float alpha)337     private void setCurrentScrimAlpha(View scrim, float alpha) {
338         if (scrim == mScrimBehind) {
339             mCurrentBehindAlpha = alpha;
340             mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
341         } else if (scrim == mScrimInFront) {
342             mCurrentInFrontAlpha = alpha;
343         } else {
344             alpha = Math.max(0.0f, Math.min(1.0f, alpha));
345             mCurrentHeadsUpAlpha = alpha;
346         }
347     }
348 
updateScrimColor(View scrim)349     protected void updateScrimColor(View scrim) {
350         float alpha1 = getCurrentScrimAlpha(scrim);
351         if (scrim instanceof ScrimView) {
352             float alpha2 = getDozeAlpha(scrim);
353             float alpha = 1 - (1 - alpha1) * (1 - alpha2);
354             alpha = Math.max(0, Math.min(1.0f, alpha));
355             int baseColor = ((ScrimView) scrim).getScrimColor();
356             ((ScrimView) scrim).setScrimColor(
357                     ColorUtils.setAlphaComponent(baseColor, (int) (alpha * 255)));
358         } else {
359             scrim.setAlpha(alpha1);
360         }
361     }
362 
startScrimAnimation(final View scrim, float target)363     private void startScrimAnimation(final View scrim, float target) {
364         float current = getCurrentScrimAlpha(scrim);
365         ValueAnimator anim = ValueAnimator.ofFloat(current, target);
366         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
367             @Override
368             public void onAnimationUpdate(ValueAnimator animation) {
369                 float alpha = (float) animation.getAnimatedValue();
370                 setCurrentScrimAlpha(scrim, alpha);
371                 updateScrimColor(scrim);
372             }
373         });
374         anim.setInterpolator(getInterpolator());
375         anim.setStartDelay(mAnimationDelay);
376         anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
377         anim.addListener(new AnimatorListenerAdapter() {
378             @Override
379             public void onAnimationEnd(Animator animation) {
380                 if (mOnAnimationFinished != null) {
381                     mOnAnimationFinished.run();
382                     mOnAnimationFinished = null;
383                 }
384                 if (mKeyguardFadingOutInProgress) {
385                     mKeyguardFadeoutAnimation = null;
386                     mKeyguardFadingOutInProgress = false;
387                 }
388                 scrim.setTag(TAG_KEY_ANIM, null);
389                 scrim.setTag(TAG_KEY_ANIM_TARGET, null);
390             }
391         });
392         anim.start();
393         if (mAnimateKeyguardFadingOut) {
394             mKeyguardFadingOutInProgress = true;
395             mKeyguardFadeoutAnimation = anim;
396         }
397         if (mSkipFirstFrame) {
398             anim.setCurrentPlayTime(16);
399         }
400         scrim.setTag(TAG_KEY_ANIM, anim);
401         scrim.setTag(TAG_KEY_ANIM_TARGET, target);
402     }
403 
getInterpolator()404     protected Interpolator getInterpolator() {
405         if (mAnimateKeyguardFadingOut && mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
406             return KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED;
407         } else if (mAnimateKeyguardFadingOut) {
408             return KEYGUARD_FADE_OUT_INTERPOLATOR;
409         } else {
410             return mInterpolator;
411         }
412     }
413 
414     @Override
onPreDraw()415     public boolean onPreDraw() {
416         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
417         mUpdatePending = false;
418         if (mDontAnimateBouncerChanges) {
419             mDontAnimateBouncerChanges = false;
420         }
421         updateScrims();
422         mDurationOverride = -1;
423         mAnimationDelay = 0;
424         mSkipFirstFrame = false;
425 
426         // Make sure that we always call the listener even if we didn't start an animation.
427         endAnimateKeyguardFadingOut(false /* force */);
428         return true;
429     }
430 
endAnimateKeyguardFadingOut(boolean force)431     private void endAnimateKeyguardFadingOut(boolean force) {
432         mAnimateKeyguardFadingOut = false;
433         if (force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind))) {
434             if (mOnAnimationFinished != null) {
435                 mOnAnimationFinished.run();
436                 mOnAnimationFinished = null;
437             }
438             mKeyguardFadingOutInProgress = false;
439         }
440     }
441 
isAnimating(View scrim)442     private boolean isAnimating(View scrim) {
443         return scrim.getTag(TAG_KEY_ANIM) != null;
444     }
445 
setDrawBehindAsSrc(boolean asSrc)446     public void setDrawBehindAsSrc(boolean asSrc) {
447         mScrimBehind.setDrawAsSrc(asSrc);
448     }
449 
450     @Override
onHeadsUpPinnedModeChanged(boolean inPinnedMode)451     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
452     }
453 
454     @Override
onHeadsUpPinned(ExpandableNotificationRow headsUp)455     public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
456         mPinnedHeadsUpCount++;
457         updateHeadsUpScrim(true);
458     }
459 
460     @Override
onHeadsUpUnPinned(ExpandableNotificationRow headsUp)461     public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
462         mPinnedHeadsUpCount--;
463         if (headsUp == mDraggedHeadsUpView) {
464             mDraggedHeadsUpView = null;
465             mTopHeadsUpDragAmount = 0.0f;
466         }
467         updateHeadsUpScrim(true);
468     }
469 
470     @Override
onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp)471     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
472     }
473 
updateHeadsUpScrim(boolean animate)474     private void updateHeadsUpScrim(boolean animate) {
475         updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha(), mCurrentHeadsUpAlpha);
476     }
477 
updateScrim(boolean animate, View scrim, float alpha, float currentAlpha)478     private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) {
479         if (mKeyguardFadingOutInProgress && mKeyguardFadeoutAnimation.getCurrentPlayTime() != 0) {
480             return;
481         }
482 
483         ValueAnimator previousAnimator = ViewState.getChildTag(scrim,
484                 TAG_KEY_ANIM);
485         float animEndValue = -1;
486         if (previousAnimator != null) {
487             if (animate || alpha == currentAlpha) {
488                 previousAnimator.cancel();
489             } else {
490                 animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
491             }
492         }
493         if (alpha != currentAlpha && alpha != animEndValue) {
494             if (animate) {
495                 startScrimAnimation(scrim, alpha);
496                 scrim.setTag(TAG_START_ALPHA, currentAlpha);
497                 scrim.setTag(TAG_END_ALPHA, alpha);
498             } else {
499                 if (previousAnimator != null) {
500                     float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA);
501                     float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
502                     // we need to increase all animation keyframes of the previous animator by the
503                     // relative change to the end value
504                     PropertyValuesHolder[] values = previousAnimator.getValues();
505                     float relativeDiff = alpha - previousEndValue;
506                     float newStartValue = previousStartValue + relativeDiff;
507                     newStartValue = Math.max(0, Math.min(1.0f, newStartValue));
508                     values[0].setFloatValues(newStartValue, alpha);
509                     scrim.setTag(TAG_START_ALPHA, newStartValue);
510                     scrim.setTag(TAG_END_ALPHA, alpha);
511                     previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
512                 } else {
513                     // update the alpha directly
514                     setCurrentScrimAlpha(scrim, alpha);
515                     updateScrimColor(scrim);
516                 }
517             }
518         }
519     }
520 
521     /**
522      * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
523      * the heads up is in its resting space and 1 means it's fully dragged out.
524      *
525      * @param draggedHeadsUpView the dragged view
526      * @param topHeadsUpDragAmount how far is it dragged
527      */
setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount)528     public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
529         mTopHeadsUpDragAmount = topHeadsUpDragAmount;
530         mDraggedHeadsUpView = draggedHeadsUpView;
531         updateHeadsUpScrim(false);
532     }
533 
calculateHeadsUpAlpha()534     private float calculateHeadsUpAlpha() {
535         float alpha;
536         if (mPinnedHeadsUpCount >= 2) {
537             alpha = 1.0f;
538         } else if (mPinnedHeadsUpCount == 0) {
539             alpha = 0.0f;
540         } else {
541             alpha = 1.0f - mTopHeadsUpDragAmount;
542         }
543         float expandFactor = (1.0f - mFraction);
544         expandFactor = Math.max(expandFactor, 0.0f);
545         return alpha * expandFactor;
546     }
547 
forceHideScrims(boolean hide)548     public void forceHideScrims(boolean hide) {
549         mForceHideScrims = hide;
550         mAnimateChange = false;
551         scheduleUpdate();
552     }
553 
dontAnimateBouncerChangesUntilNextFrame()554     public void dontAnimateBouncerChangesUntilNextFrame() {
555         mDontAnimateBouncerChanges = true;
556     }
557 
setExcludedBackgroundArea(Rect area)558     public void setExcludedBackgroundArea(Rect area) {
559         mScrimBehind.setExcludedArea(area);
560     }
561 
getScrimBehindColor()562     public int getScrimBehindColor() {
563         return mScrimBehind.getScrimColorWithAlpha();
564     }
565 
setScrimBehindChangeRunnable(Runnable changeRunnable)566     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
567         mScrimBehind.setChangeRunnable(changeRunnable);
568     }
569 
onDensityOrFontScaleChanged()570     public void onDensityOrFontScaleChanged() {
571         ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
572         layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
573                 R.dimen.heads_up_scrim_height);
574         mHeadsUpScrim.setLayoutParams(layoutParams);
575     }
576 
setCurrentUser(int currentUser)577     public void setCurrentUser(int currentUser) {
578         // Don't care in the base class.
579     }
580 }
581