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