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 com.android.systemui.R;
     20 
     21 import android.animation.Animator;
     22 import android.animation.AnimatorListenerAdapter;
     23 import android.animation.AnimatorSet;
     24 import android.animation.ValueAnimator;
     25 import android.content.Context;
     26 import android.content.res.Resources;
     27 import android.graphics.Canvas;
     28 import android.graphics.Color;
     29 import android.graphics.ColorFilter;
     30 import android.graphics.Paint;
     31 import android.graphics.PixelFormat;
     32 import android.graphics.Rect;
     33 import android.graphics.drawable.Drawable;
     34 import android.view.animation.AccelerateDecelerateInterpolator;
     35 import android.view.animation.AnimationUtils;
     36 import android.view.animation.Interpolator;
     37 
     38 public class TrustDrawable extends Drawable {
     39 
     40     private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
     41     private static final long VISIBLE_DURATION = 1000;
     42     private static final long EXIT_DURATION = 500;
     43     private static final long ENTER_DURATION = 500;
     44 
     45     private static final int ALPHA_VISIBLE_MIN = 0x26;
     46     private static final int ALPHA_VISIBLE_MAX = 0x4c;
     47 
     48     private static final int STATE_UNSET = -1;
     49     private static final int STATE_GONE = 0;
     50     private static final int STATE_ENTERING = 1;
     51     private static final int STATE_VISIBLE = 2;
     52     private static final int STATE_EXITING = 3;
     53 
     54     private int mAlpha;
     55     private boolean mAnimating;
     56 
     57     private int mCurAlpha;
     58     private float mCurInnerRadius;
     59     private Animator mCurAnimator;
     60     private int mState = STATE_UNSET;
     61     private Paint mPaint;
     62     private boolean mTrustManaged;
     63 
     64     private final float mInnerRadiusVisibleMin;
     65     private final float mInnerRadiusVisibleMax;
     66     private final float mInnerRadiusExit;
     67     private final float mInnerRadiusEnter;
     68     private final float mThickness;
     69 
     70     private final Animator mVisibleAnimator;
     71 
     72     private final Interpolator mLinearOutSlowInInterpolator;
     73     private final Interpolator mFastOutSlowInInterpolator;
     74     private final Interpolator mAccelerateDecelerateInterpolator;
     75 
     76     public TrustDrawable(Context context) {
     77         Resources r = context.getResources();
     78         mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
     79         mInnerRadiusVisibleMax = r.getDimension(R.dimen.trust_circle_inner_radius_visible_max);
     80         mInnerRadiusExit = r.getDimension(R.dimen.trust_circle_inner_radius_exit);
     81         mInnerRadiusEnter = r.getDimension(R.dimen.trust_circle_inner_radius_enter);
     82         mThickness = r.getDimension(R.dimen.trust_circle_thickness);
     83 
     84         mCurInnerRadius = mInnerRadiusEnter;
     85 
     86         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
     87                 context, android.R.interpolator.linear_out_slow_in);
     88         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
     89                 context, android.R.interpolator.fast_out_slow_in);
     90         mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
     91 
     92         mVisibleAnimator = makeVisibleAnimator();
     93 
     94         mPaint = new Paint();
     95         mPaint.setStyle(Paint.Style.STROKE);
     96         mPaint.setColor(Color.WHITE);
     97         mPaint.setAntiAlias(true);
     98         mPaint.setStrokeWidth(mThickness);
     99     }
    100 
    101     @Override
    102     public void draw(Canvas canvas) {
    103         int newAlpha = (mCurAlpha * mAlpha) / 256;
    104         if (newAlpha == 0) {
    105             return;
    106         }
    107         final Rect r = getBounds();
    108         mPaint.setAlpha(newAlpha);
    109         canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), mCurInnerRadius, mPaint);
    110     }
    111 
    112     @Override
    113     public void setAlpha(int alpha) {
    114         mAlpha = alpha;
    115     }
    116 
    117     @Override
    118     public int getAlpha() {
    119         return mAlpha;
    120     }
    121 
    122     @Override
    123     public void setColorFilter(ColorFilter cf) {
    124         throw new UnsupportedOperationException("not implemented");
    125     }
    126 
    127     @Override
    128     public int getOpacity() {
    129         return PixelFormat.TRANSLUCENT;
    130     }
    131 
    132     public void start() {
    133         if (!mAnimating) {
    134             mAnimating = true;
    135             updateState(true);
    136         }
    137     }
    138 
    139     public void stop() {
    140         if (mAnimating) {
    141             mAnimating = false;
    142             if (mCurAnimator != null) {
    143                 mCurAnimator.cancel();
    144                 mCurAnimator = null;
    145             }
    146             mState = STATE_UNSET;
    147             mCurAlpha = 0;
    148             mCurInnerRadius = mInnerRadiusEnter;
    149         }
    150     }
    151 
    152     public void setTrustManaged(boolean trustManaged) {
    153         if (trustManaged == mTrustManaged && mState != STATE_UNSET) return;
    154         mTrustManaged = trustManaged;
    155         if (mAnimating) {
    156             updateState(true);
    157         }
    158     }
    159 
    160     private void updateState(boolean animate) {
    161         int nextState = mState;
    162         if (mState == STATE_UNSET) {
    163             nextState = mTrustManaged ? STATE_ENTERING : STATE_GONE;
    164         } else if (mState == STATE_GONE) {
    165             if (mTrustManaged) nextState = STATE_ENTERING;
    166         } else if (mState == STATE_ENTERING) {
    167             if (!mTrustManaged) nextState = STATE_EXITING;
    168         } else if (mState == STATE_VISIBLE) {
    169             if (!mTrustManaged) nextState = STATE_EXITING;
    170         } else if (mState == STATE_EXITING) {
    171             if (mTrustManaged) nextState = STATE_ENTERING;
    172         }
    173         if (!animate) {
    174             if (nextState == STATE_ENTERING) nextState = STATE_VISIBLE;
    175             if (nextState == STATE_EXITING) nextState = STATE_GONE;
    176         }
    177 
    178         if (nextState != mState) {
    179             if (mCurAnimator != null) {
    180                 mCurAnimator.cancel();
    181                 mCurAnimator = null;
    182             }
    183 
    184             if (nextState == STATE_GONE) {
    185                 mCurAlpha = 0;
    186                 mCurInnerRadius = mInnerRadiusEnter;
    187             } else if (nextState == STATE_ENTERING) {
    188                 mCurAnimator = makeEnterAnimator(mCurInnerRadius, mCurAlpha);
    189                 if (mState == STATE_UNSET) {
    190                     mCurAnimator.setStartDelay(ENTERING_FROM_UNSET_START_DELAY);
    191                 }
    192             } else if (nextState == STATE_VISIBLE) {
    193                 mCurAlpha = ALPHA_VISIBLE_MAX;
    194                 mCurInnerRadius = mInnerRadiusVisibleMax;
    195                 mCurAnimator = mVisibleAnimator;
    196             } else if (nextState == STATE_EXITING) {
    197                 mCurAnimator = makeExitAnimator(mCurInnerRadius, mCurAlpha);
    198             }
    199 
    200             mState = nextState;
    201             if (mCurAnimator != null) {
    202                 mCurAnimator.start();
    203             } else {
    204                 invalidateSelf();
    205             }
    206         }
    207     }
    208 
    209     private Animator makeVisibleAnimator() {
    210         return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
    211                 ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
    212                 mAccelerateDecelerateInterpolator,
    213                 true /* repeating */, false /* stateUpdateListener */);
    214     }
    215 
    216     private Animator makeEnterAnimator(float radius, int alpha) {
    217         return makeAnimators(radius, mInnerRadiusVisibleMax,
    218                 alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator,
    219                 false /* repeating */, true /* stateUpdateListener */);
    220     }
    221 
    222     private Animator makeExitAnimator(float radius, int alpha) {
    223         return makeAnimators(radius, mInnerRadiusExit,
    224                 alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator,
    225                 false /* repeating */, true /* stateUpdateListener */);
    226     }
    227 
    228     private Animator makeAnimators(float startRadius, float endRadius,
    229             int startAlpha, int endAlpha, long duration, Interpolator interpolator,
    230             boolean repeating, boolean stateUpdateListener) {
    231         ValueAnimator alphaAnimator = configureAnimator(
    232                 ValueAnimator.ofInt(startAlpha, endAlpha),
    233                 duration, mAlphaUpdateListener, interpolator, repeating);
    234         ValueAnimator sizeAnimator = configureAnimator(
    235                 ValueAnimator.ofFloat(startRadius, endRadius),
    236                 duration, mRadiusUpdateListener, interpolator, repeating);
    237 
    238         AnimatorSet set = new AnimatorSet();
    239         set.playTogether(alphaAnimator, sizeAnimator);
    240         if (stateUpdateListener) {
    241             set.addListener(new StateUpdateAnimatorListener());
    242         }
    243         return set;
    244     }
    245 
    246     private ValueAnimator configureAnimator(ValueAnimator animator, long duration,
    247             ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator,
    248             boolean repeating) {
    249         animator.setDuration(duration);
    250         animator.addUpdateListener(updateListener);
    251         animator.setInterpolator(interpolator);
    252         if (repeating) {
    253             animator.setRepeatCount(ValueAnimator.INFINITE);
    254             animator.setRepeatMode(ValueAnimator.REVERSE);
    255         }
    256         return animator;
    257     }
    258 
    259     private final ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener =
    260             new ValueAnimator.AnimatorUpdateListener() {
    261         @Override
    262         public void onAnimationUpdate(ValueAnimator animation) {
    263             mCurAlpha = (int) animation.getAnimatedValue();
    264             invalidateSelf();
    265         }
    266     };
    267 
    268     private final ValueAnimator.AnimatorUpdateListener mRadiusUpdateListener =
    269             new ValueAnimator.AnimatorUpdateListener() {
    270         @Override
    271         public void onAnimationUpdate(ValueAnimator animation) {
    272             mCurInnerRadius = (float) animation.getAnimatedValue();
    273             invalidateSelf();
    274         }
    275     };
    276 
    277     private class StateUpdateAnimatorListener extends AnimatorListenerAdapter {
    278         boolean mCancelled;
    279 
    280         @Override
    281         public void onAnimationStart(Animator animation) {
    282             mCancelled = false;
    283         }
    284 
    285         @Override
    286         public void onAnimationCancel(Animator animation) {
    287             mCancelled = true;
    288         }
    289 
    290         @Override
    291         public void onAnimationEnd(Animator animation) {
    292             if (!mCancelled) {
    293                 updateState(false);
    294             }
    295         }
    296     }
    297 }
    298