Home | History | Annotate | Download | only in phone
      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.app.WallpaperManager;
     24 import android.content.Context;
     25 import android.graphics.Color;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Trace;
     29 import android.util.MathUtils;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.view.ViewTreeObserver;
     33 import android.view.animation.DecelerateInterpolator;
     34 import android.view.animation.Interpolator;
     35 import android.view.animation.PathInterpolator;
     36 
     37 import com.android.internal.colorextraction.ColorExtractor;
     38 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
     39 import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener;
     40 import com.android.internal.graphics.ColorUtils;
     41 import com.android.keyguard.KeyguardUpdateMonitor;
     42 import com.android.systemui.Dependency;
     43 import com.android.systemui.R;
     44 import com.android.systemui.colorextraction.SysuiColorExtractor;
     45 import com.android.systemui.statusbar.ExpandableNotificationRow;
     46 import com.android.systemui.statusbar.NotificationData;
     47 import com.android.systemui.statusbar.ScrimView;
     48 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
     49 import com.android.systemui.statusbar.stack.ViewState;
     50 
     51 import java.io.PrintWriter;
     52 import java.util.function.Consumer;
     53 
     54 /**
     55  * Controls both the scrim behind the notifications and in front of the notifications (when a
     56  * security method gets shown).
     57  */
     58 public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
     59         OnHeadsUpChangedListener, OnColorsChangedListener {
     60     public static final long ANIMATION_DURATION = 220;
     61     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
     62             = new PathInterpolator(0f, 0, 0.7f, 1f);
     63     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
     64             = new PathInterpolator(0.3f, 0f, 0.8f, 1f);
     65     // Default alpha value for most scrims, if unsure use this constant
     66     public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
     67     // A scrim varies its opacity based on a busyness factor, for example
     68     // how many notifications are currently visible.
     69     public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
     70     protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
     71     protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
     72     private static final float SCRIM_IN_FRONT_ALPHA = GRADIENT_SCRIM_ALPHA_BUSY;
     73     private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY;
     74     private static final int TAG_KEY_ANIM = R.id.scrim;
     75     private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
     76     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
     77     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
     78     private static final float NOT_INITIALIZED = -1;
     79 
     80     private final LightBarController mLightBarController;
     81     protected final ScrimView mScrimBehind;
     82     protected final ScrimView mScrimInFront;
     83     private final UnlockMethodCache mUnlockMethodCache;
     84     private final View mHeadsUpScrim;
     85     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     86 
     87     private final SysuiColorExtractor mColorExtractor;
     88     private GradientColors mLockColors;
     89     private GradientColors mSystemColors;
     90     private boolean mNeedsDrawableColorUpdate;
     91 
     92     protected float mScrimBehindAlpha;
     93     protected float mScrimBehindAlphaResValue;
     94     protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
     95     protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
     96 
     97     protected boolean mKeyguardShowing;
     98     private float mFraction;
     99 
    100     private boolean mDarkenWhileDragging;
    101     protected boolean mBouncerShowing;
    102     protected boolean mBouncerIsKeyguard = false;
    103     private boolean mWakeAndUnlocking;
    104     protected boolean mAnimateChange;
    105     private boolean mUpdatePending;
    106     private boolean mTracking;
    107     private boolean mAnimateKeyguardFadingOut;
    108     protected long mDurationOverride = -1;
    109     private long mAnimationDelay;
    110     private Runnable mOnAnimationFinished;
    111     private boolean mDeferFinishedListener;
    112     private final Interpolator mInterpolator = new DecelerateInterpolator();
    113     private boolean mDozing;
    114     private float mDozeInFrontAlpha;
    115     private float mDozeBehindAlpha;
    116     private float mCurrentInFrontAlpha  = NOT_INITIALIZED;
    117     private float mCurrentBehindAlpha = NOT_INITIALIZED;
    118     private float mCurrentHeadsUpAlpha = NOT_INITIALIZED;
    119     private int mPinnedHeadsUpCount;
    120     private float mTopHeadsUpDragAmount;
    121     private View mDraggedHeadsUpView;
    122     private boolean mForceHideScrims;
    123     private boolean mSkipFirstFrame;
    124     private boolean mDontAnimateBouncerChanges;
    125     private boolean mKeyguardFadingOutInProgress;
    126     private boolean mAnimatingDozeUnlock;
    127     private ValueAnimator mKeyguardFadeoutAnimation;
    128     /** Wake up from AOD transition is starting; need fully opaque front scrim */
    129     private boolean mWakingUpFromAodStarting;
    130     /** Wake up from AOD transition is in progress; need black tint */
    131     private boolean mWakingUpFromAodInProgress;
    132     /** Wake up from AOD transition is animating; need to reset when animation finishes */
    133     private boolean mWakingUpFromAodAnimationRunning;
    134     private boolean mScrimsVisble;
    135     private final Consumer<Boolean> mScrimVisibleListener;
    136 
    137     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
    138             ScrimView scrimInFront, View headsUpScrim,
    139             Consumer<Boolean> scrimVisibleListener) {
    140         mScrimBehind = scrimBehind;
    141         mScrimInFront = scrimInFront;
    142         mHeadsUpScrim = headsUpScrim;
    143         mScrimVisibleListener = scrimVisibleListener;
    144         final Context context = scrimBehind.getContext();
    145         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
    146         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
    147         mLightBarController = lightBarController;
    148         mScrimBehindAlphaResValue = context.getResources().getFloat(R.dimen.scrim_behind_alpha);
    149         // Scrim alpha is initially set to the value on the resource but might be changed
    150         // to make sure that text on top of it is legible.
    151         mScrimBehindAlpha = mScrimBehindAlphaResValue;
    152 
    153         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
    154         mColorExtractor.addOnColorsChangedListener(this);
    155         mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
    156                 ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
    157         mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM,
    158                 ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
    159         mNeedsDrawableColorUpdate = true;
    160 
    161         updateHeadsUpScrim(false);
    162         updateScrims();
    163     }
    164 
    165     public void setKeyguardShowing(boolean showing) {
    166         mKeyguardShowing = showing;
    167 
    168         // Showing/hiding the keyguard means that scrim colors have to be switched
    169         mNeedsDrawableColorUpdate = true;
    170         scheduleUpdate();
    171     }
    172 
    173     protected void setScrimBehindValues(float scrimBehindAlphaKeyguard,
    174             float scrimBehindAlphaUnlocking) {
    175         mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
    176         mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking;
    177         scheduleUpdate();
    178     }
    179 
    180     public void onTrackingStarted() {
    181         mTracking = true;
    182         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
    183     }
    184 
    185     public void onExpandingFinished() {
    186         mTracking = false;
    187     }
    188 
    189     public void setPanelExpansion(float fraction) {
    190         if (mFraction != fraction) {
    191             mFraction = fraction;
    192             scheduleUpdate();
    193             if (mPinnedHeadsUpCount != 0) {
    194                 updateHeadsUpScrim(false);
    195             }
    196             if (mKeyguardFadeoutAnimation != null && mTracking) {
    197                 mKeyguardFadeoutAnimation.cancel();
    198             }
    199         }
    200     }
    201 
    202     public void setBouncerShowing(boolean showing) {
    203         mBouncerShowing = showing;
    204         mAnimateChange = !mTracking && !mDontAnimateBouncerChanges && !mKeyguardFadingOutInProgress;
    205         scheduleUpdate();
    206     }
    207 
    208     /** Prepares the wakeUpFromAod animation (while turning on screen); Forces black scrims. */
    209     public void prepareWakeUpFromAod() {
    210         if (mWakingUpFromAodInProgress) {
    211             return;
    212         }
    213         mWakingUpFromAodInProgress = true;
    214         mWakingUpFromAodStarting = true;
    215         mAnimateChange = false;
    216         scheduleUpdate();
    217         onPreDraw();
    218     }
    219 
    220     /** Starts the wakeUpFromAod animation (once screen is on); animate to transparent scrims. */
    221     public void wakeUpFromAod() {
    222         if (mWakeAndUnlocking || mAnimateKeyguardFadingOut) {
    223             // Wake and unlocking has a separate transition that must not be interfered with.
    224             mWakingUpFromAodStarting = false;
    225             mWakingUpFromAodInProgress = false;
    226             return;
    227         }
    228         if (mWakingUpFromAodStarting) {
    229             mWakingUpFromAodInProgress = true;
    230             mWakingUpFromAodStarting = false;
    231             mAnimateChange = true;
    232             scheduleUpdate();
    233         }
    234     }
    235 
    236     public void setWakeAndUnlocking() {
    237         mWakeAndUnlocking = true;
    238         mAnimatingDozeUnlock = true;
    239         mWakingUpFromAodStarting = false;
    240         mWakingUpFromAodInProgress = false;
    241         scheduleUpdate();
    242     }
    243 
    244     public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished,
    245             boolean skipFirstFrame) {
    246         mWakeAndUnlocking = false;
    247         mAnimateKeyguardFadingOut = true;
    248         mDurationOverride = duration;
    249         mAnimationDelay = delay;
    250         mAnimateChange = true;
    251         mSkipFirstFrame = skipFirstFrame;
    252         mOnAnimationFinished = onAnimationFinished;
    253 
    254         if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
    255             scheduleUpdate();
    256 
    257             // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
    258             // the changes we just scheduled.
    259             onPreDraw();
    260         } else {
    261 
    262             // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
    263             // with too many things in this case, in order to not skip the initial frames.
    264             mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
    265         }
    266     }
    267 
    268     public void abortKeyguardFadingOut() {
    269         if (mAnimateKeyguardFadingOut) {
    270             endAnimateKeyguardFadingOut(true /* force */);
    271         }
    272     }
    273 
    274     public void animateKeyguardUnoccluding(long duration) {
    275         mAnimateChange = false;
    276         setScrimBehindAlpha(0f);
    277         mAnimateChange = true;
    278         scheduleUpdate();
    279         mDurationOverride = duration;
    280     }
    281 
    282     public void animateGoingToFullShade(long delay, long duration) {
    283         mDurationOverride = duration;
    284         mAnimationDelay = delay;
    285         mAnimateChange = true;
    286         scheduleUpdate();
    287     }
    288 
    289     public void setDozing(boolean dozing) {
    290         if (mDozing != dozing) {
    291             mDozing = dozing;
    292             scheduleUpdate();
    293         }
    294     }
    295 
    296     public void setDozeInFrontAlpha(float alpha) {
    297         mDozeInFrontAlpha = alpha;
    298         updateScrimColor(mScrimInFront);
    299     }
    300 
    301     public void setDozeBehindAlpha(float alpha) {
    302         mDozeBehindAlpha = alpha;
    303         updateScrimColor(mScrimBehind);
    304     }
    305 
    306     public float getDozeBehindAlpha() {
    307         return mDozeBehindAlpha;
    308     }
    309 
    310     public float getDozeInFrontAlpha() {
    311         return mDozeInFrontAlpha;
    312     }
    313 
    314     public void setNotificationCount(int notificationCount) {
    315         final float maxNotificationDensity = 3;
    316         float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
    317         float newAlpha = MathUtils.map(0, 1,
    318                 GRADIENT_SCRIM_ALPHA, GRADIENT_SCRIM_ALPHA_BUSY,
    319                 notificationDensity);
    320         if (mScrimBehindAlphaKeyguard != newAlpha) {
    321             mScrimBehindAlphaKeyguard = newAlpha;
    322             mAnimateChange = true;
    323             scheduleUpdate();
    324         }
    325     }
    326 
    327     private float getScrimInFrontAlpha() {
    328         return mKeyguardUpdateMonitor.needsSlowUnlockTransition()
    329                 ? SCRIM_IN_FRONT_ALPHA_LOCKED
    330                 : SCRIM_IN_FRONT_ALPHA;
    331     }
    332 
    333     /**
    334      * Sets the given drawable as the background of the scrim that shows up behind the
    335      * notifications.
    336      */
    337     public void setScrimBehindDrawable(Drawable drawable) {
    338         mScrimBehind.setDrawable(drawable);
    339     }
    340 
    341     protected void scheduleUpdate() {
    342         if (mUpdatePending) return;
    343 
    344         // Make sure that a frame gets scheduled.
    345         mScrimBehind.invalidate();
    346         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
    347         mUpdatePending = true;
    348     }
    349 
    350     protected void updateScrims() {
    351         // Make sure we have the right gradients and their opacities will satisfy GAR.
    352         if (mNeedsDrawableColorUpdate) {
    353             mNeedsDrawableColorUpdate = false;
    354             final GradientColors currentScrimColors;
    355             if (mKeyguardShowing) {
    356                 // Always animate color changes if we're seeing the keyguard
    357                 mScrimInFront.setColors(mLockColors, true /* animated */);
    358                 mScrimBehind.setColors(mLockColors, true /* animated */);
    359                 currentScrimColors = mLockColors;
    360             } else {
    361                 // Only animate scrim color if the scrim view is actually visible
    362                 boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0;
    363                 boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0;
    364                 mScrimInFront.setColors(mSystemColors, animateScrimInFront);
    365                 mScrimBehind.setColors(mSystemColors, animateScrimBehind);
    366                 currentScrimColors = mSystemColors;
    367             }
    368 
    369             // Calculate minimum scrim opacity for white or black text.
    370             int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE;
    371             int mainColor = currentScrimColors.getMainColor();
    372             float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor,
    373                     4.5f /* minimumContrast */) / 255f;
    374             mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity);
    375             mLightBarController.setScrimColor(mScrimInFront.getColors());
    376         }
    377 
    378         if (mAnimateKeyguardFadingOut || mForceHideScrims) {
    379             setScrimInFrontAlpha(0f);
    380             setScrimBehindAlpha(0f);
    381         } else if (mWakeAndUnlocking) {
    382             // During wake and unlock, we first hide everything behind a black scrim, which then
    383             // gets faded out from animateKeyguardFadingOut. This must never be animated.
    384             mAnimateChange = false;
    385             if (mDozing) {
    386                 setScrimInFrontAlpha(0f);
    387                 setScrimBehindAlpha(1f);
    388             } else {
    389                 setScrimInFrontAlpha(1f);
    390                 setScrimBehindAlpha(0f);
    391             }
    392         } else if (!mKeyguardShowing && !mBouncerShowing && !mWakingUpFromAodStarting) {
    393             updateScrimNormal();
    394             setScrimInFrontAlpha(0);
    395         } else {
    396             updateScrimKeyguard();
    397         }
    398         mAnimateChange = false;
    399         dispatchScrimsVisible();
    400     }
    401 
    402     private void dispatchScrimsVisible() {
    403         boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0;
    404 
    405         if (mScrimsVisble != scrimsVisible) {
    406             mScrimsVisble = scrimsVisible;
    407 
    408             mScrimVisibleListener.accept(scrimsVisible);
    409         }
    410     }
    411 
    412     private void updateScrimKeyguard() {
    413         if (mTracking && mDarkenWhileDragging) {
    414             float behindFraction = Math.max(0, Math.min(mFraction, 1));
    415             float fraction = 1 - behindFraction;
    416             fraction = (float) Math.pow(fraction, 0.8f);
    417             behindFraction = (float) Math.pow(behindFraction, 0.8f);
    418             setScrimInFrontAlpha(fraction * getScrimInFrontAlpha());
    419             setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard);
    420         } else if (mBouncerShowing && !mBouncerIsKeyguard) {
    421             setScrimInFrontAlpha(getScrimInFrontAlpha());
    422             updateScrimNormal();
    423         } else if (mBouncerShowing) {
    424             setScrimInFrontAlpha(0f);
    425             setScrimBehindAlpha(mScrimBehindAlpha);
    426         } else {
    427             float fraction = Math.max(0, Math.min(mFraction, 1));
    428             if (mWakingUpFromAodStarting) {
    429                 setScrimInFrontAlpha(1f);
    430             } else {
    431                 setScrimInFrontAlpha(0f);
    432             }
    433             setScrimBehindAlpha(fraction
    434                     * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
    435                     + mScrimBehindAlphaUnlocking);
    436         }
    437     }
    438 
    439     private void updateScrimNormal() {
    440         float frac = mFraction;
    441         // let's start this 20% of the way down the screen
    442         frac = frac * 1.2f - 0.2f;
    443         if (frac <= 0) {
    444             setScrimBehindAlpha(0);
    445         } else {
    446             // woo, special effects
    447             final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
    448             setScrimBehindAlpha(k * mScrimBehindAlpha);
    449         }
    450     }
    451 
    452     private void setScrimBehindAlpha(float alpha) {
    453         setScrimAlpha(mScrimBehind, alpha);
    454     }
    455 
    456     private void setScrimInFrontAlpha(float alpha) {
    457         setScrimAlpha(mScrimInFront, alpha);
    458         if (alpha == 0f) {
    459             mScrimInFront.setClickable(false);
    460         } else {
    461             // Eat touch events (unless dozing).
    462             mScrimInFront.setClickable(!mDozing);
    463         }
    464     }
    465 
    466     private void setScrimAlpha(View scrim, float alpha) {
    467         updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim));
    468     }
    469 
    470     protected float getDozeAlpha(View scrim) {
    471         return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha;
    472     }
    473 
    474     protected float getCurrentScrimAlpha(View scrim) {
    475         return scrim == mScrimBehind ? mCurrentBehindAlpha
    476                 : scrim == mScrimInFront ? mCurrentInFrontAlpha
    477                 : mCurrentHeadsUpAlpha;
    478     }
    479 
    480     private void setCurrentScrimAlpha(View scrim, float alpha) {
    481         if (scrim == mScrimBehind) {
    482             mCurrentBehindAlpha = alpha;
    483             mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
    484         } else if (scrim == mScrimInFront) {
    485             mCurrentInFrontAlpha = alpha;
    486         } else {
    487             alpha = Math.max(0.0f, Math.min(1.0f, alpha));
    488             mCurrentHeadsUpAlpha = alpha;
    489         }
    490     }
    491 
    492     private void updateScrimColor(View scrim) {
    493         float alpha1 = getCurrentScrimAlpha(scrim);
    494         if (scrim instanceof ScrimView) {
    495             ScrimView scrimView = (ScrimView) scrim;
    496             float dozeAlpha = getDozeAlpha(scrim);
    497             float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha);
    498             alpha = Math.max(0, Math.min(1.0f, alpha));
    499             scrimView.setViewAlpha(alpha);
    500 
    501             Trace.traceCounter(Trace.TRACE_TAG_APP,
    502                     scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha",
    503                     (int) (alpha * 255));
    504 
    505             int dozeTint = Color.TRANSPARENT;
    506 
    507             boolean dozing = mAnimatingDozeUnlock || mDozing;
    508             boolean frontScrimDozing = mWakingUpFromAodInProgress;
    509             if (dozing || frontScrimDozing && scrim == mScrimInFront) {
    510                 dozeTint = Color.BLACK;
    511             }
    512             Trace.traceCounter(Trace.TRACE_TAG_APP,
    513                     scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint",
    514                     dozeTint == Color.BLACK ? 1 : 0);
    515 
    516             scrimView.setTint(dozeTint);
    517         } else {
    518             scrim.setAlpha(alpha1);
    519         }
    520         dispatchScrimsVisible();
    521     }
    522 
    523     private void startScrimAnimation(final View scrim, float target) {
    524         float current = getCurrentScrimAlpha(scrim);
    525         ValueAnimator anim = ValueAnimator.ofFloat(current, target);
    526         anim.addUpdateListener(animation -> {
    527             float alpha = (float) animation.getAnimatedValue();
    528             setCurrentScrimAlpha(scrim, alpha);
    529             updateScrimColor(scrim);
    530             dispatchScrimsVisible();
    531         });
    532         anim.setInterpolator(getInterpolator());
    533         anim.setStartDelay(mAnimationDelay);
    534         anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
    535         anim.addListener(new AnimatorListenerAdapter() {
    536             @Override
    537             public void onAnimationEnd(Animator animation) {
    538                 if (!mDeferFinishedListener && mOnAnimationFinished != null) {
    539                     mOnAnimationFinished.run();
    540                     mOnAnimationFinished = null;
    541                 }
    542                 if (mKeyguardFadingOutInProgress) {
    543                     mKeyguardFadeoutAnimation = null;
    544                     mKeyguardFadingOutInProgress = false;
    545                     mAnimatingDozeUnlock = false;
    546                 }
    547                 if (mWakingUpFromAodAnimationRunning && !mDeferFinishedListener) {
    548                     mWakingUpFromAodAnimationRunning = false;
    549                     mWakingUpFromAodInProgress = false;
    550                 }
    551                 scrim.setTag(TAG_KEY_ANIM, null);
    552                 scrim.setTag(TAG_KEY_ANIM_TARGET, null);
    553                 dispatchScrimsVisible();
    554             }
    555         });
    556         anim.start();
    557         if (mAnimateKeyguardFadingOut) {
    558             mKeyguardFadingOutInProgress = true;
    559             mKeyguardFadeoutAnimation = anim;
    560         }
    561         if (mWakingUpFromAodInProgress) {
    562             mWakingUpFromAodAnimationRunning = true;
    563         }
    564         if (mSkipFirstFrame) {
    565             anim.setCurrentPlayTime(16);
    566         }
    567         scrim.setTag(TAG_KEY_ANIM, anim);
    568         scrim.setTag(TAG_KEY_ANIM_TARGET, target);
    569     }
    570 
    571     protected Interpolator getInterpolator() {
    572         if (mAnimateKeyguardFadingOut && mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
    573             return KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED;
    574         } else if (mAnimateKeyguardFadingOut) {
    575             return KEYGUARD_FADE_OUT_INTERPOLATOR;
    576         } else {
    577             return mInterpolator;
    578         }
    579     }
    580 
    581     @Override
    582     public boolean onPreDraw() {
    583         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
    584         mUpdatePending = false;
    585         if (mDontAnimateBouncerChanges) {
    586             mDontAnimateBouncerChanges = false;
    587         }
    588         updateScrims();
    589         mDurationOverride = -1;
    590         mAnimationDelay = 0;
    591         mSkipFirstFrame = false;
    592 
    593         // Make sure that we always call the listener even if we didn't start an animation.
    594         endAnimateKeyguardFadingOut(false /* force */);
    595         return true;
    596     }
    597 
    598     private void endAnimateKeyguardFadingOut(boolean force) {
    599         mAnimateKeyguardFadingOut = false;
    600         if (force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind))) {
    601             if (mOnAnimationFinished != null) {
    602                 mOnAnimationFinished.run();
    603                 mOnAnimationFinished = null;
    604             }
    605             mKeyguardFadingOutInProgress = false;
    606             if (!mWakeAndUnlocking || force)
    607                 mAnimatingDozeUnlock = false;
    608         }
    609     }
    610 
    611     private boolean isAnimating(View scrim) {
    612         return scrim.getTag(TAG_KEY_ANIM) != null;
    613     }
    614 
    615     public void setDrawBehindAsSrc(boolean asSrc) {
    616         mScrimBehind.setDrawAsSrc(asSrc);
    617     }
    618 
    619     @Override
    620     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
    621     }
    622 
    623     @Override
    624     public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
    625         mPinnedHeadsUpCount++;
    626         updateHeadsUpScrim(true);
    627     }
    628 
    629     @Override
    630     public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
    631         mPinnedHeadsUpCount--;
    632         if (headsUp == mDraggedHeadsUpView) {
    633             mDraggedHeadsUpView = null;
    634             mTopHeadsUpDragAmount = 0.0f;
    635         }
    636         updateHeadsUpScrim(true);
    637     }
    638 
    639     @Override
    640     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
    641     }
    642 
    643     private void updateHeadsUpScrim(boolean animate) {
    644         updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha(), mCurrentHeadsUpAlpha);
    645     }
    646 
    647     private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) {
    648         if (mKeyguardFadingOutInProgress && mKeyguardFadeoutAnimation.getCurrentPlayTime() != 0) {
    649             return;
    650         }
    651 
    652         ValueAnimator previousAnimator = ViewState.getChildTag(scrim,
    653                 TAG_KEY_ANIM);
    654         float animEndValue = -1;
    655         if (previousAnimator != null) {
    656             if (animate || alpha == currentAlpha) {
    657                 // We are not done yet! Defer calling the finished listener.
    658                 if (animate) {
    659                     mDeferFinishedListener = true;
    660                 }
    661                 previousAnimator.cancel();
    662                 mDeferFinishedListener = false;
    663             } else {
    664                 animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
    665             }
    666         }
    667         if (alpha != currentAlpha && alpha != animEndValue) {
    668             if (animate) {
    669                 startScrimAnimation(scrim, alpha);
    670                 scrim.setTag(TAG_START_ALPHA, currentAlpha);
    671                 scrim.setTag(TAG_END_ALPHA, alpha);
    672             } else {
    673                 if (previousAnimator != null) {
    674                     float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA);
    675                     float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
    676                     // we need to increase all animation keyframes of the previous animator by the
    677                     // relative change to the end value
    678                     PropertyValuesHolder[] values = previousAnimator.getValues();
    679                     float relativeDiff = alpha - previousEndValue;
    680                     float newStartValue = previousStartValue + relativeDiff;
    681                     newStartValue = Math.max(0, Math.min(1.0f, newStartValue));
    682                     values[0].setFloatValues(newStartValue, alpha);
    683                     scrim.setTag(TAG_START_ALPHA, newStartValue);
    684                     scrim.setTag(TAG_END_ALPHA, alpha);
    685                     previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    686                 } else {
    687                     // update the alpha directly
    688                     setCurrentScrimAlpha(scrim, alpha);
    689                     updateScrimColor(scrim);
    690                 }
    691             }
    692         }
    693     }
    694 
    695     /**
    696      * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
    697      * the heads up is in its resting space and 1 means it's fully dragged out.
    698      *
    699      * @param draggedHeadsUpView the dragged view
    700      * @param topHeadsUpDragAmount how far is it dragged
    701      */
    702     public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
    703         mTopHeadsUpDragAmount = topHeadsUpDragAmount;
    704         mDraggedHeadsUpView = draggedHeadsUpView;
    705         updateHeadsUpScrim(false);
    706     }
    707 
    708     private float calculateHeadsUpAlpha() {
    709         float alpha;
    710         if (mPinnedHeadsUpCount >= 2) {
    711             alpha = 1.0f;
    712         } else if (mPinnedHeadsUpCount == 0) {
    713             alpha = 0.0f;
    714         } else {
    715             alpha = 1.0f - mTopHeadsUpDragAmount;
    716         }
    717         float expandFactor = (1.0f - mFraction);
    718         expandFactor = Math.max(expandFactor, 0.0f);
    719         return alpha * expandFactor;
    720     }
    721 
    722     public void forceHideScrims(boolean hide, boolean animated) {
    723         mForceHideScrims = hide;
    724         mAnimateChange = animated;
    725         scheduleUpdate();
    726     }
    727 
    728     public void dontAnimateBouncerChangesUntilNextFrame() {
    729         mDontAnimateBouncerChanges = true;
    730     }
    731 
    732     public void setExcludedBackgroundArea(Rect area) {
    733         mScrimBehind.setExcludedArea(area);
    734     }
    735 
    736     public int getBackgroundColor() {
    737         int color = mLockColors.getMainColor();
    738         return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)),
    739                 Color.red(color), Color.green(color), Color.blue(color));
    740     }
    741 
    742     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
    743         mScrimBehind.setChangeRunnable(changeRunnable);
    744     }
    745 
    746     public void onDensityOrFontScaleChanged() {
    747         ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
    748         layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
    749                 R.dimen.heads_up_scrim_height);
    750         mHeadsUpScrim.setLayoutParams(layoutParams);
    751     }
    752 
    753     public void setCurrentUser(int currentUser) {
    754         // Don't care in the base class.
    755     }
    756 
    757     @Override
    758     public void onColorsChanged(ColorExtractor colorExtractor, int which) {
    759         if ((which & WallpaperManager.FLAG_LOCK) != 0) {
    760             mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
    761                     ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
    762             mNeedsDrawableColorUpdate = true;
    763             scheduleUpdate();
    764         }
    765         if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
    766             mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM,
    767                     ColorExtractor.TYPE_DARK, mKeyguardShowing);
    768             mNeedsDrawableColorUpdate = true;
    769             scheduleUpdate();
    770         }
    771     }
    772 
    773     public void dump(PrintWriter pw) {
    774         pw.println(" ScrimController:");
    775 
    776         pw.print("   frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
    777         pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
    778         pw.print(" dozeAlpha="); pw.print(mDozeInFrontAlpha);
    779         pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
    780 
    781         pw.print("   backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
    782         pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
    783         pw.print(" dozeAlpha="); pw.print(mDozeBehindAlpha);
    784         pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
    785 
    786         pw.print("   mBouncerShowing="); pw.println(mBouncerShowing);
    787         pw.print("   mTracking="); pw.println(mTracking);
    788         pw.print("   mForceHideScrims="); pw.println(mForceHideScrims);
    789     }
    790 }
    791