TrustDrawable.java revision 2e3ccbb37e21eb40308f83fb185209773147f78d
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
17package com.android.systemui.statusbar.phone;
18
19import com.android.systemui.R;
20
21import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.AnimatorSet;
24import android.animation.ValueAnimator;
25import android.content.Context;
26import android.content.res.Resources;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.ColorFilter;
30import android.graphics.Paint;
31import android.graphics.PixelFormat;
32import android.graphics.Rect;
33import android.graphics.drawable.Drawable;
34import android.view.animation.AccelerateDecelerateInterpolator;
35import android.view.animation.AnimationUtils;
36import android.view.animation.Interpolator;
37
38public 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