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.content.Context;
     24 import android.graphics.Color;
     25 import android.util.Log;
     26 import android.view.View;
     27 import android.view.ViewTreeObserver;
     28 import android.view.animation.DecelerateInterpolator;
     29 import android.view.animation.Interpolator;
     30 import android.view.animation.PathInterpolator;
     31 
     32 import com.android.systemui.R;
     33 import com.android.systemui.statusbar.BackDropView;
     34 import com.android.systemui.statusbar.ExpandableNotificationRow;
     35 import com.android.systemui.statusbar.NotificationData;
     36 import com.android.systemui.statusbar.ScrimView;
     37 import com.android.systemui.statusbar.policy.HeadsUpManager;
     38 import com.android.systemui.statusbar.stack.StackStateAnimator;
     39 
     40 /**
     41  * Controls both the scrim behind the notifications and in front of the notifications (when a
     42  * security method gets shown).
     43  */
     44 public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
     45         HeadsUpManager.OnHeadsUpChangedListener {
     46     public static final long ANIMATION_DURATION = 220;
     47 
     48     private static final float SCRIM_BEHIND_ALPHA = 0.62f;
     49     private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
     50     private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
     51     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
     52     private static final int TAG_KEY_ANIM = R.id.scrim;
     53     private static final int TAG_HUN_START_ALPHA = R.id.hun_scrim_alpha_start;
     54     private static final int TAG_HUN_END_ALPHA = R.id.hun_scrim_alpha_end;
     55 
     56     private final ScrimView mScrimBehind;
     57     private final ScrimView mScrimInFront;
     58     private final UnlockMethodCache mUnlockMethodCache;
     59     private final View mHeadsUpScrim;
     60 
     61     private boolean mKeyguardShowing;
     62     private float mFraction;
     63 
     64     private boolean mDarkenWhileDragging;
     65     private boolean mBouncerShowing;
     66     private boolean mWakeAndUnlocking;
     67     private boolean mAnimateChange;
     68     private boolean mUpdatePending;
     69     private boolean mExpanding;
     70     private boolean mAnimateKeyguardFadingOut;
     71     private long mDurationOverride = -1;
     72     private long mAnimationDelay;
     73     private Runnable mOnAnimationFinished;
     74     private boolean mAnimationStarted;
     75     private final Interpolator mInterpolator = new DecelerateInterpolator();
     76     private final Interpolator mKeyguardFadeOutInterpolator = new PathInterpolator(0f, 0, 0.7f, 1f);
     77     private BackDropView mBackDropView;
     78     private boolean mScrimSrcEnabled;
     79     private boolean mDozing;
     80     private float mDozeInFrontAlpha;
     81     private float mDozeBehindAlpha;
     82     private float mCurrentInFrontAlpha;
     83     private float mCurrentBehindAlpha;
     84     private float mCurrentHeadsUpAlpha = 1;
     85     private int mPinnedHeadsUpCount;
     86     private float mTopHeadsUpDragAmount;
     87     private View mDraggedHeadsUpView;
     88     private boolean mForceHideScrims;
     89 
     90     public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
     91             boolean scrimSrcEnabled) {
     92         mScrimBehind = scrimBehind;
     93         mScrimInFront = scrimInFront;
     94         mHeadsUpScrim = headsUpScrim;
     95         final Context context = scrimBehind.getContext();
     96         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
     97         mScrimSrcEnabled = scrimSrcEnabled;
     98         updateHeadsUpScrim(false);
     99     }
    100 
    101     public void setKeyguardShowing(boolean showing) {
    102         mKeyguardShowing = showing;
    103         scheduleUpdate();
    104     }
    105 
    106     public void onTrackingStarted() {
    107         mExpanding = true;
    108         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
    109     }
    110 
    111     public void onExpandingFinished() {
    112         mExpanding = false;
    113     }
    114 
    115     public void setPanelExpansion(float fraction) {
    116         if (mFraction != fraction) {
    117             mFraction = fraction;
    118             scheduleUpdate();
    119             if (mPinnedHeadsUpCount != 0) {
    120                 updateHeadsUpScrim(false);
    121             }
    122         }
    123     }
    124 
    125     public void setBouncerShowing(boolean showing) {
    126         mBouncerShowing = showing;
    127         mAnimateChange = !mExpanding;
    128         scheduleUpdate();
    129     }
    130 
    131     public void setWakeAndUnlocking() {
    132         mWakeAndUnlocking = true;
    133         scheduleUpdate();
    134     }
    135 
    136     public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
    137         mWakeAndUnlocking = false;
    138         mAnimateKeyguardFadingOut = true;
    139         mDurationOverride = duration;
    140         mAnimationDelay = delay;
    141         mAnimateChange = true;
    142         mOnAnimationFinished = onAnimationFinished;
    143         scheduleUpdate();
    144     }
    145 
    146     public void abortKeyguardFadingOut() {
    147         if (mAnimateKeyguardFadingOut) {
    148             endAnimateKeyguardFadingOut();
    149         }
    150     }
    151 
    152     public void animateGoingToFullShade(long delay, long duration) {
    153         mDurationOverride = duration;
    154         mAnimationDelay = delay;
    155         mAnimateChange = true;
    156         scheduleUpdate();
    157     }
    158 
    159     public void setDozing(boolean dozing) {
    160         if (mDozing != dozing) {
    161             mDozing = dozing;
    162             scheduleUpdate();
    163         }
    164     }
    165 
    166     public void setDozeInFrontAlpha(float alpha) {
    167         mDozeInFrontAlpha = alpha;
    168         updateScrimColor(mScrimInFront);
    169     }
    170 
    171     public void setDozeBehindAlpha(float alpha) {
    172         mDozeBehindAlpha = alpha;
    173         updateScrimColor(mScrimBehind);
    174     }
    175 
    176     public float getDozeBehindAlpha() {
    177         return mDozeBehindAlpha;
    178     }
    179 
    180     public float getDozeInFrontAlpha() {
    181         return mDozeInFrontAlpha;
    182     }
    183 
    184     private void scheduleUpdate() {
    185         if (mUpdatePending) return;
    186 
    187         // Make sure that a frame gets scheduled.
    188         mScrimBehind.invalidate();
    189         mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
    190         mUpdatePending = true;
    191     }
    192 
    193     private void updateScrims() {
    194         if (mAnimateKeyguardFadingOut || mForceHideScrims) {
    195             setScrimInFrontColor(0f);
    196             setScrimBehindColor(0f);
    197         } else if (mWakeAndUnlocking) {
    198 
    199             // During wake and unlock, we first hide everything behind a black scrim, which then
    200             // gets faded out from animateKeyguardFadingOut.
    201             setScrimInFrontColor(1f);
    202             setScrimBehindColor(0f);
    203         } else if (!mKeyguardShowing && !mBouncerShowing) {
    204             updateScrimNormal();
    205             setScrimInFrontColor(0);
    206         } else {
    207             updateScrimKeyguard();
    208         }
    209         mAnimateChange = false;
    210     }
    211 
    212     private void updateScrimKeyguard() {
    213         if (mExpanding && mDarkenWhileDragging) {
    214             float behindFraction = Math.max(0, Math.min(mFraction, 1));
    215             float fraction = 1 - behindFraction;
    216             fraction = (float) Math.pow(fraction, 0.8f);
    217             behindFraction = (float) Math.pow(behindFraction, 0.8f);
    218             setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
    219             setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
    220         } else if (mBouncerShowing) {
    221             setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
    222             setScrimBehindColor(0f);
    223         } else {
    224             float fraction = Math.max(0, Math.min(mFraction, 1));
    225             setScrimInFrontColor(0f);
    226             setScrimBehindColor(fraction
    227                     * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING)
    228                     + SCRIM_BEHIND_ALPHA_UNLOCKING);
    229         }
    230     }
    231 
    232     private void updateScrimNormal() {
    233         float frac = mFraction;
    234         // let's start this 20% of the way down the screen
    235         frac = frac * 1.2f - 0.2f;
    236         if (frac <= 0) {
    237             setScrimBehindColor(0);
    238         } else {
    239             // woo, special effects
    240             final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
    241             setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
    242         }
    243     }
    244 
    245     private void setScrimBehindColor(float alpha) {
    246         setScrimColor(mScrimBehind, alpha);
    247     }
    248 
    249     private void setScrimInFrontColor(float alpha) {
    250         setScrimColor(mScrimInFront, alpha);
    251         if (alpha == 0f) {
    252             mScrimInFront.setClickable(false);
    253         } else {
    254 
    255             // Eat touch events (unless dozing).
    256             mScrimInFront.setClickable(!mDozing);
    257         }
    258     }
    259 
    260     private void setScrimColor(View scrim, float alpha) {
    261         Object runningAnim = scrim.getTag(TAG_KEY_ANIM);
    262         if (runningAnim instanceof ValueAnimator) {
    263             ((ValueAnimator) runningAnim).cancel();
    264             scrim.setTag(TAG_KEY_ANIM, null);
    265         }
    266         if (mAnimateChange) {
    267             startScrimAnimation(scrim, alpha);
    268         } else {
    269             setCurrentScrimAlpha(scrim, alpha);
    270             updateScrimColor(scrim);
    271         }
    272     }
    273 
    274     private float getDozeAlpha(View scrim) {
    275         return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha;
    276     }
    277 
    278     private float getCurrentScrimAlpha(View scrim) {
    279         return scrim == mScrimBehind ? mCurrentBehindAlpha
    280                 : scrim == mScrimInFront ? mCurrentInFrontAlpha
    281                 : mCurrentHeadsUpAlpha;
    282     }
    283 
    284     private void setCurrentScrimAlpha(View scrim, float alpha) {
    285         if (scrim == mScrimBehind) {
    286             mCurrentBehindAlpha = alpha;
    287         } else if (scrim == mScrimInFront) {
    288             mCurrentInFrontAlpha = alpha;
    289         } else {
    290             alpha = Math.max(0.0f, Math.min(1.0f, alpha));
    291             mCurrentHeadsUpAlpha = alpha;
    292         }
    293     }
    294 
    295     private void updateScrimColor(View scrim) {
    296         float alpha1 = getCurrentScrimAlpha(scrim);
    297         if (scrim instanceof ScrimView) {
    298             float alpha2 = getDozeAlpha(scrim);
    299             float alpha = 1 - (1 - alpha1) * (1 - alpha2);
    300             ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
    301         } else {
    302             scrim.setAlpha(alpha1);
    303         }
    304     }
    305 
    306     private void startScrimAnimation(final View scrim, float target) {
    307         float current = getCurrentScrimAlpha(scrim);
    308         ValueAnimator anim = ValueAnimator.ofFloat(current, target);
    309         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    310             @Override
    311             public void onAnimationUpdate(ValueAnimator animation) {
    312                 float alpha = (float) animation.getAnimatedValue();
    313                 setCurrentScrimAlpha(scrim, alpha);
    314                 updateScrimColor(scrim);
    315             }
    316         });
    317         anim.setInterpolator(getInterpolator());
    318         anim.setStartDelay(mAnimationDelay);
    319         anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
    320         anim.addListener(new AnimatorListenerAdapter() {
    321             @Override
    322             public void onAnimationEnd(Animator animation) {
    323                 if (mOnAnimationFinished != null) {
    324                     mOnAnimationFinished.run();
    325                     mOnAnimationFinished = null;
    326                 }
    327                 scrim.setTag(TAG_KEY_ANIM, null);
    328             }
    329         });
    330         anim.start();
    331         scrim.setTag(TAG_KEY_ANIM, anim);
    332         mAnimationStarted = true;
    333     }
    334 
    335     private Interpolator getInterpolator() {
    336         return mAnimateKeyguardFadingOut ? mKeyguardFadeOutInterpolator : mInterpolator;
    337     }
    338 
    339     @Override
    340     public boolean onPreDraw() {
    341         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
    342         mUpdatePending = false;
    343         updateScrims();
    344         mDurationOverride = -1;
    345         mAnimationDelay = 0;
    346 
    347         // Make sure that we always call the listener even if we didn't start an animation.
    348         endAnimateKeyguardFadingOut();
    349         mAnimationStarted = false;
    350         return true;
    351     }
    352 
    353     private void endAnimateKeyguardFadingOut() {
    354         mAnimateKeyguardFadingOut = false;
    355         if (!mAnimationStarted && mOnAnimationFinished != null) {
    356             mOnAnimationFinished.run();
    357             mOnAnimationFinished = null;
    358         }
    359     }
    360 
    361     public void setBackDropView(BackDropView backDropView) {
    362         mBackDropView = backDropView;
    363         mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
    364             @Override
    365             public void run() {
    366                 updateScrimBehindDrawingMode();
    367             }
    368         });
    369         updateScrimBehindDrawingMode();
    370     }
    371 
    372     private void updateScrimBehindDrawingMode() {
    373         boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled;
    374         mScrimBehind.setDrawAsSrc(asSrc);
    375     }
    376 
    377     @Override
    378     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
    379     }
    380 
    381     @Override
    382     public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
    383         mPinnedHeadsUpCount++;
    384         updateHeadsUpScrim(true);
    385     }
    386 
    387     @Override
    388     public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
    389         mPinnedHeadsUpCount--;
    390         if (headsUp == mDraggedHeadsUpView) {
    391             mDraggedHeadsUpView = null;
    392             mTopHeadsUpDragAmount = 0.0f;
    393         }
    394         updateHeadsUpScrim(true);
    395     }
    396 
    397     @Override
    398     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
    399     }
    400 
    401     private void updateHeadsUpScrim(boolean animate) {
    402         float alpha = calculateHeadsUpAlpha();
    403         ValueAnimator previousAnimator = StackStateAnimator.getChildTag(mHeadsUpScrim,
    404                 TAG_KEY_ANIM);
    405         float animEndValue = -1;
    406         if (previousAnimator != null) {
    407             if (animate || alpha == mCurrentHeadsUpAlpha) {
    408                 previousAnimator.cancel();
    409             } else {
    410                 animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA);
    411             }
    412         }
    413         if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) {
    414             if (animate) {
    415                 startScrimAnimation(mHeadsUpScrim, alpha);
    416                 mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, mCurrentHeadsUpAlpha);
    417                 mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha);
    418             } else {
    419                 if (previousAnimator != null) {
    420                     float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
    421                             TAG_HUN_START_ALPHA);
    422                     float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
    423                            TAG_HUN_END_ALPHA);
    424                     // we need to increase all animation keyframes of the previous animator by the
    425                     // relative change to the end value
    426                     PropertyValuesHolder[] values = previousAnimator.getValues();
    427                     float relativeDiff = alpha - previousEndValue;
    428                     float newStartValue = previousStartValue + relativeDiff;
    429                     values[0].setFloatValues(newStartValue, alpha);
    430                     mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, newStartValue);
    431                     mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha);
    432                     previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    433                 } else {
    434                     // update the alpha directly
    435                     setCurrentScrimAlpha(mHeadsUpScrim, alpha);
    436                     updateScrimColor(mHeadsUpScrim);
    437                 }
    438             }
    439         }
    440     }
    441 
    442     /**
    443      * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
    444      * the heads up is in its resting space and 1 means it's fully dragged out.
    445      *
    446      * @param draggedHeadsUpView the dragged view
    447      * @param topHeadsUpDragAmount how far is it dragged
    448      */
    449     public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
    450         mTopHeadsUpDragAmount = topHeadsUpDragAmount;
    451         mDraggedHeadsUpView = draggedHeadsUpView;
    452         updateHeadsUpScrim(false);
    453     }
    454 
    455     private float calculateHeadsUpAlpha() {
    456         float alpha;
    457         if (mPinnedHeadsUpCount >= 2) {
    458             alpha = 1.0f;
    459         } else if (mPinnedHeadsUpCount == 0) {
    460             alpha = 0.0f;
    461         } else {
    462             alpha = 1.0f - mTopHeadsUpDragAmount;
    463         }
    464         float expandFactor = (1.0f - mFraction);
    465         expandFactor = Math.max(expandFactor, 0.0f);
    466         return alpha * expandFactor;
    467     }
    468 
    469     public void forceHideScrims(boolean hide) {
    470         mForceHideScrims = hide;
    471         mAnimateChange = false;
    472         scheduleUpdate();
    473     }
    474 }
    475