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