blob: e12b5744499340417364d1d6788d84ae9b3fdc93 [file] [log] [blame]
Selim Cinekbaa23272014-07-08 18:01:07 +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;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ArgbEvaluator;
22import android.animation.PropertyValuesHolder;
23import android.animation.ValueAnimator;
Jason Monkca215682017-02-09 14:32:54 -080024import android.annotation.Nullable;
Selim Cinekbaa23272014-07-08 18:01:07 +020025import android.content.Context;
Lucas Dupin00a95942017-05-13 16:08:06 -070026import android.content.res.TypedArray;
Selim Cinekbaa23272014-07-08 18:01:07 +020027import android.graphics.Canvas;
Selim Cinek72bcaa22015-06-03 15:32:16 +020028import android.graphics.CanvasProperty;
Selim Cinekbaa23272014-07-08 18:01:07 +020029import android.graphics.Color;
30import android.graphics.Paint;
31import android.graphics.PorterDuff;
32import android.graphics.drawable.Drawable;
33import android.util.AttributeSet;
Selim Cinek72bcaa22015-06-03 15:32:16 +020034import android.view.DisplayListCanvas;
35import android.view.RenderNodeAnimator;
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +020036import android.view.View;
37import android.view.ViewAnimationUtils;
Selim Cinekbaa23272014-07-08 18:01:07 +020038import android.view.animation.Interpolator;
39import android.widget.ImageView;
Selim Cinek372d1bd2015-08-14 13:19:37 -070040
Winsonc0d70582016-01-29 10:24:39 -080041import com.android.systemui.Interpolators;
Selim Cinekbaa23272014-07-08 18:01:07 +020042import com.android.systemui.R;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070043import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
Selim Cinekbaa23272014-07-08 18:01:07 +020044
45/**
46 * An ImageView which does not have overlapping renderings commands and therefore does not need a
47 * layer when alpha is changed.
48 */
49public class KeyguardAffordanceView extends ImageView {
50
51 private static final long CIRCLE_APPEAR_DURATION = 80;
52 private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
53 private static final long NORMAL_ANIMATION_DURATION = 200;
54 public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
55 public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
56
57 private final int mMinBackgroundRadius;
58 private final Paint mCirclePaint;
Lucas Dupin00a95942017-05-13 16:08:06 -070059 private final int mDarkIconColor;
Selim Cinekbaa23272014-07-08 18:01:07 +020060 private final int mNormalColor;
61 private final ArgbEvaluator mColorInterpolator;
62 private final FlingAnimationUtils mFlingAnimationUtils;
Selim Cinekbaa23272014-07-08 18:01:07 +020063 private float mCircleRadius;
64 private int mCenterX;
65 private int mCenterY;
66 private ValueAnimator mCircleAnimator;
67 private ValueAnimator mAlphaAnimator;
68 private ValueAnimator mScaleAnimator;
Selim Cinekbaa23272014-07-08 18:01:07 +020069 private float mCircleStartValue;
70 private boolean mCircleWillBeHidden;
71 private int[] mTempPoint = new int[2];
Adrian Roosa4eba9f2015-07-22 18:13:04 -070072 private float mImageScale = 1f;
Selim Cinekbaa23272014-07-08 18:01:07 +020073 private int mCircleColor;
74 private boolean mIsLeft;
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +020075 private View mPreviewView;
76 private float mCircleStartRadius;
77 private float mMaxCircleSize;
78 private Animator mPreviewClipper;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070079 private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
Selim Cinek72bcaa22015-06-03 15:32:16 +020080 private boolean mSupportHardware;
81 private boolean mFinishing;
Selim Cinek372d1bd2015-08-14 13:19:37 -070082 private boolean mLaunchingAffordance;
Jason Monkca215682017-02-09 14:32:54 -080083 private boolean mShouldTint = true;
Selim Cinek72bcaa22015-06-03 15:32:16 +020084
85 private CanvasProperty<Float> mHwCircleRadius;
86 private CanvasProperty<Float> mHwCenterX;
87 private CanvasProperty<Float> mHwCenterY;
88 private CanvasProperty<Paint> mHwCirclePaint;
89
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +020090 private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
91 @Override
92 public void onAnimationEnd(Animator animation) {
93 mPreviewClipper = null;
94 }
95 };
Selim Cinekbaa23272014-07-08 18:01:07 +020096 private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
97 @Override
98 public void onAnimationEnd(Animator animation) {
99 mCircleAnimator = null;
100 }
101 };
102 private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() {
103 @Override
104 public void onAnimationEnd(Animator animation) {
105 mScaleAnimator = null;
106 }
107 };
108 private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() {
109 @Override
110 public void onAnimationEnd(Animator animation) {
111 mAlphaAnimator = null;
112 }
113 };
Selim Cinekbaa23272014-07-08 18:01:07 +0200114
115 public KeyguardAffordanceView(Context context) {
116 this(context, null);
117 }
118
119 public KeyguardAffordanceView(Context context, AttributeSet attrs) {
120 this(context, attrs, 0);
121 }
122
123 public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
124 this(context, attrs, defStyleAttr, 0);
125 }
126
127 public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr,
128 int defStyleRes) {
129 super(context, attrs, defStyleAttr, defStyleRes);
Lucas Dupin00a95942017-05-13 16:08:06 -0700130 TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.ImageView);
131
Selim Cinekbaa23272014-07-08 18:01:07 +0200132 mCirclePaint = new Paint();
133 mCirclePaint.setAntiAlias(true);
134 mCircleColor = 0xffffffff;
135 mCirclePaint.setColor(mCircleColor);
136
Lucas Dupin00a95942017-05-13 16:08:06 -0700137 mNormalColor = a.getColor(android.R.styleable.ImageView_tint, 0xffffffff);
138 mDarkIconColor = 0xff000000;
Selim Cinekbaa23272014-07-08 18:01:07 +0200139 mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
140 R.dimen.keyguard_affordance_min_background_radius);
Selim Cinekbaa23272014-07-08 18:01:07 +0200141 mColorInterpolator = new ArgbEvaluator();
142 mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
Lucas Dupin00a95942017-05-13 16:08:06 -0700143
144 a.recycle();
Selim Cinekbaa23272014-07-08 18:01:07 +0200145 }
146
Jason Monkca215682017-02-09 14:32:54 -0800147 public void setImageDrawable(@Nullable Drawable drawable, boolean tint) {
148 super.setImageDrawable(drawable);
149 mShouldTint = tint;
150 updateIconColor();
151 }
152
Selim Cinekbaa23272014-07-08 18:01:07 +0200153 @Override
154 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
155 super.onLayout(changed, left, top, right, bottom);
156 mCenterX = getWidth() / 2;
157 mCenterY = getHeight() / 2;
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200158 mMaxCircleSize = getMaxCircleSize();
Selim Cinekbaa23272014-07-08 18:01:07 +0200159 }
160
161 @Override
162 protected void onDraw(Canvas canvas) {
Selim Cinek4ae263c2016-08-08 17:27:24 -0700163 mSupportHardware = canvas.isHardwareAccelerated();
Selim Cinekbaa23272014-07-08 18:01:07 +0200164 drawBackgroundCircle(canvas);
Selim Cinekbaa23272014-07-08 18:01:07 +0200165 canvas.save();
Selim Cinekbaa23272014-07-08 18:01:07 +0200166 canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
167 super.onDraw(canvas);
168 canvas.restore();
169 }
170
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200171 public void setPreviewView(View v) {
Selim Cinek372d1bd2015-08-14 13:19:37 -0700172 View oldPreviewView = mPreviewView;
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200173 mPreviewView = v;
Selim Cinek0d61b512014-08-28 14:51:05 +0200174 if (mPreviewView != null) {
Selim Cinek372d1bd2015-08-14 13:19:37 -0700175 mPreviewView.setVisibility(mLaunchingAffordance
176 ? oldPreviewView.getVisibility() : INVISIBLE);
Selim Cinek0d61b512014-08-28 14:51:05 +0200177 }
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200178 }
179
Selim Cinekbaa23272014-07-08 18:01:07 +0200180 private void updateIconColor() {
Jason Monkca215682017-02-09 14:32:54 -0800181 if (!mShouldTint) return;
Selim Cinekbaa23272014-07-08 18:01:07 +0200182 Drawable drawable = getDrawable().mutate();
183 float alpha = mCircleRadius / mMinBackgroundRadius;
184 alpha = Math.min(1.0f, alpha);
Lucas Dupin00a95942017-05-13 16:08:06 -0700185 int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mDarkIconColor);
Selim Cinekbaa23272014-07-08 18:01:07 +0200186 drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
187 }
188
189 private void drawBackgroundCircle(Canvas canvas) {
Selim Cinek372d1bd2015-08-14 13:19:37 -0700190 if (mCircleRadius > 0 || mFinishing) {
Selim Cinek2c984f12016-09-06 16:23:17 -0700191 if (mFinishing && mSupportHardware && mHwCenterX != null) {
192 // Our hardware drawing proparties can be null if the finishing started but we have
193 // never drawn before. In that case we are not doing a render thread animation
194 // anyway, so we need to use the normal drawing.
Selim Cinek72bcaa22015-06-03 15:32:16 +0200195 DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
196 displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
197 mHwCirclePaint);
198 } else {
199 updateCircleColor();
200 canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
201 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200202 }
203 }
204
205 private void updateCircleColor() {
206 float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
207 (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
Selim Cinek6746c282015-04-21 19:58:31 -0700208 if (mPreviewView != null && mPreviewView.getVisibility() == VISIBLE) {
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200209 float finishingFraction = 1 - Math.max(0, mCircleRadius - mCircleStartRadius)
210 / (mMaxCircleSize - mCircleStartRadius);
211 fraction *= finishingFraction;
212 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200213 int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
214 Color.red(mCircleColor),
215 Color.green(mCircleColor), Color.blue(mCircleColor));
216 mCirclePaint.setColor(color);
217 }
218
219 public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
220 cancelAnimator(mCircleAnimator);
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200221 cancelAnimator(mPreviewClipper);
Selim Cinek72bcaa22015-06-03 15:32:16 +0200222 mFinishing = true;
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200223 mCircleStartRadius = mCircleRadius;
Selim Cinek372d1bd2015-08-14 13:19:37 -0700224 final float maxCircleSize = getMaxCircleSize();
Selim Cinek72bcaa22015-06-03 15:32:16 +0200225 Animator animatorToRadius;
226 if (mSupportHardware) {
227 initHwProperties();
228 animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
Selim Cinek372d1bd2015-08-14 13:19:37 -0700229 startRtAlphaFadeIn();
Selim Cinek72bcaa22015-06-03 15:32:16 +0200230 } else {
231 animatorToRadius = getAnimatorToRadius(maxCircleSize);
232 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200233 mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
234 velocity, maxCircleSize);
235 animatorToRadius.addListener(new AnimatorListenerAdapter() {
236 @Override
237 public void onAnimationEnd(Animator animation) {
238 mAnimationEndRunnable.run();
Selim Cinek72bcaa22015-06-03 15:32:16 +0200239 mFinishing = false;
Selim Cinek372d1bd2015-08-14 13:19:37 -0700240 mCircleRadius = maxCircleSize;
241 invalidate();
Selim Cinekbaa23272014-07-08 18:01:07 +0200242 }
243 });
244 animatorToRadius.start();
245 setImageAlpha(0, true);
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200246 if (mPreviewView != null) {
247 mPreviewView.setVisibility(View.VISIBLE);
248 mPreviewClipper = ViewAnimationUtils.createCircularReveal(
249 mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
250 maxCircleSize);
251 mFlingAnimationUtils.applyDismissing(mPreviewClipper, mCircleRadius, maxCircleSize,
252 velocity, maxCircleSize);
253 mPreviewClipper.addListener(mClipEndListener);
254 mPreviewClipper.start();
Selim Cinek72bcaa22015-06-03 15:32:16 +0200255 if (mSupportHardware) {
256 startRtCircleFadeOut(animatorToRadius.getDuration());
257 }
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200258 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200259 }
260
Selim Cinek372d1bd2015-08-14 13:19:37 -0700261 /**
262 * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
263 * alpha 0 in the beginning.
264 */
265 private void startRtAlphaFadeIn() {
266 if (mCircleRadius == 0 && mPreviewView == null) {
267 Paint modifiedPaint = new Paint(mCirclePaint);
268 modifiedPaint.setColor(mCircleColor);
269 modifiedPaint.setAlpha(0);
270 mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
271 RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
272 RenderNodeAnimator.PAINT_ALPHA, 255);
273 animator.setTarget(this);
Selim Cinekc18010f2016-01-20 13:41:30 -0800274 animator.setInterpolator(Interpolators.ALPHA_IN);
Selim Cinek372d1bd2015-08-14 13:19:37 -0700275 animator.setDuration(250);
276 animator.start();
277 }
278 }
279
280 public void instantFinishAnimation() {
281 cancelAnimator(mPreviewClipper);
282 if (mPreviewView != null) {
283 mPreviewView.setClipBounds(null);
284 mPreviewView.setVisibility(View.VISIBLE);
285 }
286 mCircleRadius = getMaxCircleSize();
287 setImageAlpha(0, false);
288 invalidate();
289 }
290
Selim Cinek72bcaa22015-06-03 15:32:16 +0200291 private void startRtCircleFadeOut(long duration) {
292 RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
293 RenderNodeAnimator.PAINT_ALPHA, 0);
294 animator.setDuration(duration);
Selim Cinekc18010f2016-01-20 13:41:30 -0800295 animator.setInterpolator(Interpolators.ALPHA_OUT);
Selim Cinek72bcaa22015-06-03 15:32:16 +0200296 animator.setTarget(this);
297 animator.start();
298 }
299
300 private Animator getRtAnimatorToRadius(float circleRadius) {
301 RenderNodeAnimator animator = new RenderNodeAnimator(mHwCircleRadius, circleRadius);
302 animator.setTarget(this);
303 return animator;
304 }
305
306 private void initHwProperties() {
307 mHwCenterX = CanvasProperty.createFloat(mCenterX);
308 mHwCenterY = CanvasProperty.createFloat(mCenterY);
309 mHwCirclePaint = CanvasProperty.createPaint(mCirclePaint);
310 mHwCircleRadius = CanvasProperty.createFloat(mCircleRadius);
311 }
312
Selim Cinekbaa23272014-07-08 18:01:07 +0200313 private float getMaxCircleSize() {
314 getLocationInWindow(mTempPoint);
315 float rootWidth = getRootView().getWidth();
316 float width = mTempPoint[0] + mCenterX;
317 width = Math.max(rootWidth - width, width);
318 float height = mTempPoint[1] + mCenterY;
319 return (float) Math.hypot(width, height);
320 }
321
322 public void setCircleRadius(float circleRadius) {
Selim Cinek5386fb32014-09-03 16:37:36 +0200323 setCircleRadius(circleRadius, false, false);
324 }
325
326 public void setCircleRadius(float circleRadius, boolean slowAnimation) {
327 setCircleRadius(circleRadius, slowAnimation, false);
Selim Cinekbaa23272014-07-08 18:01:07 +0200328 }
329
330 public void setCircleRadiusWithoutAnimation(float circleRadius) {
331 cancelAnimator(mCircleAnimator);
Selim Cinek5386fb32014-09-03 16:37:36 +0200332 setCircleRadius(circleRadius, false ,true);
Selim Cinekbaa23272014-07-08 18:01:07 +0200333 }
334
Selim Cinek5386fb32014-09-03 16:37:36 +0200335 private void setCircleRadius(float circleRadius, boolean slowAnimation, boolean noAnimation) {
Selim Cinekbaa23272014-07-08 18:01:07 +0200336
337 // Check if we need a new animation
338 boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden)
339 || (mCircleAnimator == null && mCircleRadius == 0.0f);
340 boolean nowHidden = circleRadius == 0.0f;
341 boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
342 if (!radiusNeedsAnimation) {
343 if (mCircleAnimator == null) {
344 mCircleRadius = circleRadius;
Jorim Jaggi0b75f832014-08-07 02:20:27 +0200345 updateIconColor();
Selim Cinekbaa23272014-07-08 18:01:07 +0200346 invalidate();
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200347 if (nowHidden) {
348 if (mPreviewView != null) {
349 mPreviewView.setVisibility(View.INVISIBLE);
350 }
351 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200352 } else if (!mCircleWillBeHidden) {
353
354 // We just update the end value
355 float diff = circleRadius - mMinBackgroundRadius;
356 PropertyValuesHolder[] values = mCircleAnimator.getValues();
357 values[0].setFloatValues(mCircleStartValue + diff, circleRadius);
358 mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
359 }
360 } else {
361 cancelAnimator(mCircleAnimator);
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200362 cancelAnimator(mPreviewClipper);
Selim Cinekbaa23272014-07-08 18:01:07 +0200363 ValueAnimator animator = getAnimatorToRadius(circleRadius);
364 Interpolator interpolator = circleRadius == 0.0f
Selim Cinekc18010f2016-01-20 13:41:30 -0800365 ? Interpolators.FAST_OUT_LINEAR_IN
366 : Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinekbaa23272014-07-08 18:01:07 +0200367 animator.setInterpolator(interpolator);
Selim Cinek5386fb32014-09-03 16:37:36 +0200368 long duration = 250;
369 if (!slowAnimation) {
370 float durationFactor = Math.abs(mCircleRadius - circleRadius)
371 / (float) mMinBackgroundRadius;
372 duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
373 duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
374 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200375 animator.setDuration(duration);
376 animator.start();
Selim Cinek0d61b512014-08-28 14:51:05 +0200377 if (mPreviewView != null && mPreviewView.getVisibility() == View.VISIBLE) {
Jorim Jaggib6cdcbc2014-07-25 21:58:29 +0200378 mPreviewView.setVisibility(View.VISIBLE);
379 mPreviewClipper = ViewAnimationUtils.createCircularReveal(
380 mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
381 circleRadius);
382 mPreviewClipper.setInterpolator(interpolator);
383 mPreviewClipper.setDuration(duration);
384 mPreviewClipper.addListener(mClipEndListener);
385 mPreviewClipper.addListener(new AnimatorListenerAdapter() {
386 @Override
387 public void onAnimationEnd(Animator animation) {
388 mPreviewView.setVisibility(View.INVISIBLE);
389 }
390 });
391 mPreviewClipper.start();
392 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200393 }
394 }
395
396 private ValueAnimator getAnimatorToRadius(float circleRadius) {
397 ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius);
398 mCircleAnimator = animator;
399 mCircleStartValue = mCircleRadius;
400 mCircleWillBeHidden = circleRadius == 0.0f;
401 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
402 @Override
403 public void onAnimationUpdate(ValueAnimator animation) {
404 mCircleRadius = (float) animation.getAnimatedValue();
Jorim Jaggi0b75f832014-08-07 02:20:27 +0200405 updateIconColor();
Selim Cinekbaa23272014-07-08 18:01:07 +0200406 invalidate();
407 }
408 });
409 animator.addListener(mCircleEndListener);
410 return animator;
411 }
412
413 private void cancelAnimator(Animator animator) {
414 if (animator != null) {
415 animator.cancel();
416 }
417 }
418
419 public void setImageScale(float imageScale, boolean animate) {
420 setImageScale(imageScale, animate, -1, null);
421 }
422
423 /**
424 * Sets the scale of the containing image
425 *
426 * @param imageScale The new Scale.
427 * @param animate Should an animation be performed
428 * @param duration If animate, whats the duration? When -1 we take the default duration
429 * @param interpolator If animate, whats the interpolator? When null we take the default
430 * interpolator.
431 */
432 public void setImageScale(float imageScale, boolean animate, long duration,
433 Interpolator interpolator) {
434 cancelAnimator(mScaleAnimator);
435 if (!animate) {
436 mImageScale = imageScale;
437 invalidate();
438 } else {
439 ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale);
440 mScaleAnimator = animator;
441 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
442 @Override
443 public void onAnimationUpdate(ValueAnimator animation) {
444 mImageScale = (float) animation.getAnimatedValue();
445 invalidate();
446 }
447 });
448 animator.addListener(mScaleEndListener);
449 if (interpolator == null) {
450 interpolator = imageScale == 0.0f
Selim Cinekc18010f2016-01-20 13:41:30 -0800451 ? Interpolators.FAST_OUT_LINEAR_IN
452 : Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinekbaa23272014-07-08 18:01:07 +0200453 }
454 animator.setInterpolator(interpolator);
455 if (duration == -1) {
456 float durationFactor = Math.abs(mImageScale - imageScale)
457 / (1.0f - MIN_ICON_SCALE_AMOUNT);
458 durationFactor = Math.min(1.0f, durationFactor);
459 duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
460 }
461 animator.setDuration(duration);
462 animator.start();
463 }
464 }
465
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700466 public void setRestingAlpha(float alpha) {
467 mRestingAlpha = alpha;
468
469 // TODO: Handle the case an animation is playing.
470 setImageAlpha(alpha, false);
471 }
472
473 public float getRestingAlpha() {
474 return mRestingAlpha;
475 }
476
Selim Cinekbaa23272014-07-08 18:01:07 +0200477 public void setImageAlpha(float alpha, boolean animate) {
478 setImageAlpha(alpha, animate, -1, null, null);
479 }
480
481 /**
482 * Sets the alpha of the containing image
483 *
484 * @param alpha The new alpha.
485 * @param animate Should an animation be performed
486 * @param duration If animate, whats the duration? When -1 we take the default duration
487 * @param interpolator If animate, whats the interpolator? When null we take the default
488 * interpolator.
489 */
490 public void setImageAlpha(float alpha, boolean animate, long duration,
491 Interpolator interpolator, Runnable runnable) {
492 cancelAnimator(mAlphaAnimator);
Selim Cinek372d1bd2015-08-14 13:19:37 -0700493 alpha = mLaunchingAffordance ? 0 : alpha;
Selim Cinekbaa23272014-07-08 18:01:07 +0200494 int endAlpha = (int) (alpha * 255);
Adrian Roos4ebcdfd2014-08-12 23:33:49 +0200495 final Drawable background = getBackground();
Selim Cinekbaa23272014-07-08 18:01:07 +0200496 if (!animate) {
Adrian Roos4ebcdfd2014-08-12 23:33:49 +0200497 if (background != null) background.mutate().setAlpha(endAlpha);
Selim Cinekbaa23272014-07-08 18:01:07 +0200498 setImageAlpha(endAlpha);
499 } else {
500 int currentAlpha = getImageAlpha();
501 ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
502 mAlphaAnimator = animator;
503 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
504 @Override
505 public void onAnimationUpdate(ValueAnimator animation) {
Adrian Roos4ebcdfd2014-08-12 23:33:49 +0200506 int alpha = (int) animation.getAnimatedValue();
507 if (background != null) background.mutate().setAlpha(alpha);
508 setImageAlpha(alpha);
Selim Cinekbaa23272014-07-08 18:01:07 +0200509 }
510 });
511 animator.addListener(mAlphaEndListener);
512 if (interpolator == null) {
513 interpolator = alpha == 0.0f
Selim Cinekc18010f2016-01-20 13:41:30 -0800514 ? Interpolators.FAST_OUT_LINEAR_IN
515 : Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinekbaa23272014-07-08 18:01:07 +0200516 }
517 animator.setInterpolator(interpolator);
518 if (duration == -1) {
519 float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
520 durationFactor = Math.min(1.0f, durationFactor);
521 duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
522 }
523 animator.setDuration(duration);
524 if (runnable != null) {
525 animator.addListener(getEndListener(runnable));
526 }
527 animator.start();
528 }
529 }
530
531 private Animator.AnimatorListener getEndListener(final Runnable runnable) {
532 return new AnimatorListenerAdapter() {
533 boolean mCancelled;
534 @Override
535 public void onAnimationCancel(Animator animation) {
536 mCancelled = true;
537 }
538
539 @Override
540 public void onAnimationEnd(Animator animation) {
541 if (!mCancelled) {
542 runnable.run();
543 }
544 }
545 };
546 }
547
548 public float getCircleRadius() {
549 return mCircleRadius;
550 }
551
Adrian Roos38fc76062014-09-22 17:34:56 +0200552 @Override
553 public boolean performClick() {
554 if (isClickable()) {
555 return super.performClick();
556 } else {
557 return false;
558 }
559 }
Selim Cinek372d1bd2015-08-14 13:19:37 -0700560
561 public void setLaunchingAffordance(boolean launchingAffordance) {
562 mLaunchingAffordance = launchingAffordance;
563 }
Selim Cinekbaa23272014-07-08 18:01:07 +0200564}