blob: b89aa8fa936b495902f79116907637b7a47d0560 [file] [log] [blame]
Adrian Roos2e3ccbb2014-08-19 22:23:37 +02001/*
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);
Adrian Roos1540da42014-10-15 14:56:28 +0200136 invalidateSelf();
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200137 }
138 }
139
140 public void stop() {
141 if (mAnimating) {
142 mAnimating = false;
143 if (mCurAnimator != null) {
144 mCurAnimator.cancel();
145 mCurAnimator = null;
146 }
147 mState = STATE_UNSET;
148 mCurAlpha = 0;
149 mCurInnerRadius = mInnerRadiusEnter;
Adrian Roos1540da42014-10-15 14:56:28 +0200150 invalidateSelf();
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200151 }
152 }
153
154 public void setTrustManaged(boolean trustManaged) {
155 if (trustManaged == mTrustManaged && mState != STATE_UNSET) return;
156 mTrustManaged = trustManaged;
Adrian Roos1540da42014-10-15 14:56:28 +0200157 updateState(true);
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200158 }
159
Adrian Roos1540da42014-10-15 14:56:28 +0200160 private void updateState(boolean allowTransientState) {
161 if (!mAnimating) {
162 return;
163 }
164
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200165 int nextState = mState;
166 if (mState == STATE_UNSET) {
167 nextState = mTrustManaged ? STATE_ENTERING : STATE_GONE;
168 } else if (mState == STATE_GONE) {
169 if (mTrustManaged) nextState = STATE_ENTERING;
170 } else if (mState == STATE_ENTERING) {
171 if (!mTrustManaged) nextState = STATE_EXITING;
172 } else if (mState == STATE_VISIBLE) {
173 if (!mTrustManaged) nextState = STATE_EXITING;
174 } else if (mState == STATE_EXITING) {
175 if (mTrustManaged) nextState = STATE_ENTERING;
176 }
Adrian Roos1540da42014-10-15 14:56:28 +0200177 if (!allowTransientState) {
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200178 if (nextState == STATE_ENTERING) nextState = STATE_VISIBLE;
179 if (nextState == STATE_EXITING) nextState = STATE_GONE;
180 }
181
182 if (nextState != mState) {
183 if (mCurAnimator != null) {
184 mCurAnimator.cancel();
185 mCurAnimator = null;
186 }
187
188 if (nextState == STATE_GONE) {
189 mCurAlpha = 0;
190 mCurInnerRadius = mInnerRadiusEnter;
191 } else if (nextState == STATE_ENTERING) {
192 mCurAnimator = makeEnterAnimator(mCurInnerRadius, mCurAlpha);
193 if (mState == STATE_UNSET) {
194 mCurAnimator.setStartDelay(ENTERING_FROM_UNSET_START_DELAY);
195 }
196 } else if (nextState == STATE_VISIBLE) {
197 mCurAlpha = ALPHA_VISIBLE_MAX;
198 mCurInnerRadius = mInnerRadiusVisibleMax;
199 mCurAnimator = mVisibleAnimator;
200 } else if (nextState == STATE_EXITING) {
201 mCurAnimator = makeExitAnimator(mCurInnerRadius, mCurAlpha);
202 }
203
204 mState = nextState;
205 if (mCurAnimator != null) {
206 mCurAnimator.start();
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200207 }
Adrian Roos1540da42014-10-15 14:56:28 +0200208 invalidateSelf();
Adrian Roos2e3ccbb2014-08-19 22:23:37 +0200209 }
210 }
211
212 private Animator makeVisibleAnimator() {
213 return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
214 ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
215 mAccelerateDecelerateInterpolator,
216 true /* repeating */, false /* stateUpdateListener */);
217 }
218
219 private Animator makeEnterAnimator(float radius, int alpha) {
220 return makeAnimators(radius, mInnerRadiusVisibleMax,
221 alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator,
222 false /* repeating */, true /* stateUpdateListener */);
223 }
224
225 private Animator makeExitAnimator(float radius, int alpha) {
226 return makeAnimators(radius, mInnerRadiusExit,
227 alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator,
228 false /* repeating */, true /* stateUpdateListener */);
229 }
230
231 private Animator makeAnimators(float startRadius, float endRadius,
232 int startAlpha, int endAlpha, long duration, Interpolator interpolator,
233 boolean repeating, boolean stateUpdateListener) {
234 ValueAnimator alphaAnimator = configureAnimator(
235 ValueAnimator.ofInt(startAlpha, endAlpha),
236 duration, mAlphaUpdateListener, interpolator, repeating);
237 ValueAnimator sizeAnimator = configureAnimator(
238 ValueAnimator.ofFloat(startRadius, endRadius),
239 duration, mRadiusUpdateListener, interpolator, repeating);
240
241 AnimatorSet set = new AnimatorSet();
242 set.playTogether(alphaAnimator, sizeAnimator);
243 if (stateUpdateListener) {
244 set.addListener(new StateUpdateAnimatorListener());
245 }
246 return set;
247 }
248
249 private ValueAnimator configureAnimator(ValueAnimator animator, long duration,
250 ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator,
251 boolean repeating) {
252 animator.setDuration(duration);
253 animator.addUpdateListener(updateListener);
254 animator.setInterpolator(interpolator);
255 if (repeating) {
256 animator.setRepeatCount(ValueAnimator.INFINITE);
257 animator.setRepeatMode(ValueAnimator.REVERSE);
258 }
259 return animator;
260 }
261
262 private final ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener =
263 new ValueAnimator.AnimatorUpdateListener() {
264 @Override
265 public void onAnimationUpdate(ValueAnimator animation) {
266 mCurAlpha = (int) animation.getAnimatedValue();
267 invalidateSelf();
268 }
269 };
270
271 private final ValueAnimator.AnimatorUpdateListener mRadiusUpdateListener =
272 new ValueAnimator.AnimatorUpdateListener() {
273 @Override
274 public void onAnimationUpdate(ValueAnimator animation) {
275 mCurInnerRadius = (float) animation.getAnimatedValue();
276 invalidateSelf();
277 }
278 };
279
280 private class StateUpdateAnimatorListener extends AnimatorListenerAdapter {
281 boolean mCancelled;
282
283 @Override
284 public void onAnimationStart(Animator animation) {
285 mCancelled = false;
286 }
287
288 @Override
289 public void onAnimationCancel(Animator animation) {
290 mCancelled = true;
291 }
292
293 @Override
294 public void onAnimationEnd(Animator animation) {
295 if (!mCancelled) {
296 updateState(false);
297 }
298 }
299 }
300}