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 TrustDrawable(Context context)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 draw(Canvas canvas)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 setAlpha(int alpha)113 public void setAlpha(int alpha) { 114 mAlpha = alpha; 115 } 116 117 @Override getAlpha()118 public int getAlpha() { 119 return mAlpha; 120 } 121 122 @Override setColorFilter(ColorFilter colorFilter)123 public void setColorFilter(ColorFilter colorFilter) { 124 throw new UnsupportedOperationException("not implemented"); 125 } 126 127 @Override getOpacity()128 public int getOpacity() { 129 return PixelFormat.TRANSLUCENT; 130 } 131 start()132 public void start() { 133 if (!mAnimating) { 134 mAnimating = true; 135 updateState(true); 136 invalidateSelf(); 137 } 138 } 139 stop()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; 150 invalidateSelf(); 151 } 152 } 153 setTrustManaged(boolean trustManaged)154 public void setTrustManaged(boolean trustManaged) { 155 if (trustManaged == mTrustManaged && mState != STATE_UNSET) return; 156 mTrustManaged = trustManaged; 157 updateState(true); 158 } 159 updateState(boolean allowTransientState)160 private void updateState(boolean allowTransientState) { 161 if (!mAnimating) { 162 return; 163 } 164 165 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 } 177 if (!allowTransientState) { 178 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(); 207 } 208 invalidateSelf(); 209 } 210 } 211 makeVisibleAnimator()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 makeEnterAnimator(float radius, int alpha)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 makeExitAnimator(float radius, int alpha)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 makeAnimators(float startRadius, float endRadius, int startAlpha, int endAlpha, long duration, Interpolator interpolator, boolean repeating, boolean stateUpdateListener)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 configureAnimator(ValueAnimator animator, long duration, ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator, boolean repeating)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 onAnimationStart(Animator animation)284 public void onAnimationStart(Animator animation) { 285 mCancelled = false; 286 } 287 288 @Override onAnimationCancel(Animator animation)289 public void onAnimationCancel(Animator animation) { 290 mCancelled = true; 291 } 292 293 @Override onAnimationEnd(Animator animation)294 public void onAnimationEnd(Animator animation) { 295 if (!mCancelled) { 296 updateState(false); 297 } 298 } 299 } 300 } 301