1baa23274596246d03741457701ac515a73aa8818Selim Cinek/*
2baa23274596246d03741457701ac515a73aa8818Selim Cinek * Copyright (C) 2014 The Android Open Source Project
3baa23274596246d03741457701ac515a73aa8818Selim Cinek *
4baa23274596246d03741457701ac515a73aa8818Selim Cinek * Licensed under the Apache License, Version 2.0 (the "License");
5baa23274596246d03741457701ac515a73aa8818Selim Cinek * you may not use this file except in compliance with the License.
6baa23274596246d03741457701ac515a73aa8818Selim Cinek * You may obtain a copy of the License at
7baa23274596246d03741457701ac515a73aa8818Selim Cinek *
8baa23274596246d03741457701ac515a73aa8818Selim Cinek *      http://www.apache.org/licenses/LICENSE-2.0
9baa23274596246d03741457701ac515a73aa8818Selim Cinek *
10baa23274596246d03741457701ac515a73aa8818Selim Cinek * Unless required by applicable law or agreed to in writing, software
11baa23274596246d03741457701ac515a73aa8818Selim Cinek * distributed under the License is distributed on an "AS IS" BASIS,
12baa23274596246d03741457701ac515a73aa8818Selim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13baa23274596246d03741457701ac515a73aa8818Selim Cinek * See the License for the specific language governing permissions and
14baa23274596246d03741457701ac515a73aa8818Selim Cinek * limitations under the License
15baa23274596246d03741457701ac515a73aa8818Selim Cinek */
16baa23274596246d03741457701ac515a73aa8818Selim Cinek
17baa23274596246d03741457701ac515a73aa8818Selim Cinekpackage com.android.systemui.statusbar;
18baa23274596246d03741457701ac515a73aa8818Selim Cinek
19baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.animation.Animator;
20baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.animation.AnimatorListenerAdapter;
21baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.animation.ArgbEvaluator;
22baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.animation.PropertyValuesHolder;
23baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.animation.ValueAnimator;
24baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.content.Context;
25baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.Canvas;
26baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.Color;
27baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.Paint;
28baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.PorterDuff;
29baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.drawable.Drawable;
30baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.util.AttributeSet;
31b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.View;
32b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.ViewAnimationUtils;
33baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.view.animation.AnimationUtils;
34baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.view.animation.Interpolator;
35baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.widget.ImageView;
36baa23274596246d03741457701ac515a73aa8818Selim Cinekimport com.android.systemui.R;
37baa23274596246d03741457701ac515a73aa8818Selim Cinek
38baa23274596246d03741457701ac515a73aa8818Selim Cinek/**
39baa23274596246d03741457701ac515a73aa8818Selim Cinek * An ImageView which does not have overlapping renderings commands and therefore does not need a
40baa23274596246d03741457701ac515a73aa8818Selim Cinek * layer when alpha is changed.
41baa23274596246d03741457701ac515a73aa8818Selim Cinek */
42baa23274596246d03741457701ac515a73aa8818Selim Cinekpublic class KeyguardAffordanceView extends ImageView {
43baa23274596246d03741457701ac515a73aa8818Selim Cinek
44baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long CIRCLE_APPEAR_DURATION = 80;
45baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
46baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long NORMAL_ANIMATION_DURATION = 200;
47baa23274596246d03741457701ac515a73aa8818Selim Cinek    public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
48baa23274596246d03741457701ac515a73aa8818Selim Cinek    public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
49baa23274596246d03741457701ac515a73aa8818Selim Cinek
50baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mMinBackgroundRadius;
51baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final Paint mCirclePaint;
52baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final Interpolator mAppearInterpolator;
53baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final Interpolator mDisappearInterpolator;
54baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mInverseColor;
55baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mNormalColor;
56baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final ArgbEvaluator mColorInterpolator;
57baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final FlingAnimationUtils mFlingAnimationUtils;
58baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final Drawable mArrowDrawable;
59baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mHintChevronPadding;
60baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mCircleRadius;
61baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCenterX;
62baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCenterY;
63baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mCircleAnimator;
64baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mAlphaAnimator;
65baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mScaleAnimator;
66baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mArrowAnimator;
67baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mCircleStartValue;
68baa23274596246d03741457701ac515a73aa8818Selim Cinek    private boolean mCircleWillBeHidden;
69baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int[] mTempPoint = new int[2];
70baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mImageScale;
71baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCircleColor;
72baa23274596246d03741457701ac515a73aa8818Selim Cinek    private boolean mIsLeft;
73baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mArrowAlpha = 0.0f;
74b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private View mPreviewView;
75b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private float mCircleStartRadius;
76b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private float mMaxCircleSize;
77b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private Animator mPreviewClipper;
78b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
79b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        @Override
80b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        public void onAnimationEnd(Animator animation) {
81b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewClipper = null;
82b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
83b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    };
84baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
85baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
86baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
87baa23274596246d03741457701ac515a73aa8818Selim Cinek            mCircleAnimator = null;
88baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
89baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
90baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() {
91baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
92baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
93baa23274596246d03741457701ac515a73aa8818Selim Cinek            mScaleAnimator = null;
94baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
95baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
96baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() {
97baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
98baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
99baa23274596246d03741457701ac515a73aa8818Selim Cinek            mAlphaAnimator = null;
100baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
101baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
102baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mArrowEndListener = new AnimatorListenerAdapter() {
103baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
104baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
105baa23274596246d03741457701ac515a73aa8818Selim Cinek            mArrowAnimator = null;
106baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
107baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
108baa23274596246d03741457701ac515a73aa8818Selim Cinek
109baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context) {
110baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, null);
111baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
112baa23274596246d03741457701ac515a73aa8818Selim Cinek
113baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs) {
114baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, attrs, 0);
115baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
116baa23274596246d03741457701ac515a73aa8818Selim Cinek
117baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
118baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, attrs, defStyleAttr, 0);
119baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
120baa23274596246d03741457701ac515a73aa8818Selim Cinek
121baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr,
122baa23274596246d03741457701ac515a73aa8818Selim Cinek            int defStyleRes) {
123baa23274596246d03741457701ac515a73aa8818Selim Cinek        super(context, attrs, defStyleAttr, defStyleRes);
124baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint = new Paint();
125baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setAntiAlias(true);
126baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleColor = 0xffffffff;
127baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setColor(mCircleColor);
128baa23274596246d03741457701ac515a73aa8818Selim Cinek
129baa23274596246d03741457701ac515a73aa8818Selim Cinek        mNormalColor = 0xffffffff;
130baa23274596246d03741457701ac515a73aa8818Selim Cinek        mInverseColor = 0xff000000;
131baa23274596246d03741457701ac515a73aa8818Selim Cinek        mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
132baa23274596246d03741457701ac515a73aa8818Selim Cinek                R.dimen.keyguard_affordance_min_background_radius);
133baa23274596246d03741457701ac515a73aa8818Selim Cinek        mHintChevronPadding = mContext.getResources().getDimensionPixelSize(
134baa23274596246d03741457701ac515a73aa8818Selim Cinek                R.dimen.hint_chevron_circle_padding);
135baa23274596246d03741457701ac515a73aa8818Selim Cinek        mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
136baa23274596246d03741457701ac515a73aa8818Selim Cinek                android.R.interpolator.linear_out_slow_in);
137baa23274596246d03741457701ac515a73aa8818Selim Cinek        mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
138baa23274596246d03741457701ac515a73aa8818Selim Cinek                android.R.interpolator.fast_out_linear_in);
139baa23274596246d03741457701ac515a73aa8818Selim Cinek        mColorInterpolator = new ArgbEvaluator();
140baa23274596246d03741457701ac515a73aa8818Selim Cinek        mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
141baa23274596246d03741457701ac515a73aa8818Selim Cinek        mArrowDrawable = context.getDrawable(R.drawable.ic_chevron_left);
142baa23274596246d03741457701ac515a73aa8818Selim Cinek        mArrowDrawable.setBounds(0, 0, mArrowDrawable.getIntrinsicWidth(),
143baa23274596246d03741457701ac515a73aa8818Selim Cinek                mArrowDrawable.getIntrinsicHeight());
144baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
145baa23274596246d03741457701ac515a73aa8818Selim Cinek
146baa23274596246d03741457701ac515a73aa8818Selim Cinek    @Override
147baa23274596246d03741457701ac515a73aa8818Selim Cinek    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
148baa23274596246d03741457701ac515a73aa8818Selim Cinek        super.onLayout(changed, left, top, right, bottom);
149baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCenterX = getWidth() / 2;
150baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCenterY = getHeight() / 2;
151b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mMaxCircleSize = getMaxCircleSize();
152baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
153baa23274596246d03741457701ac515a73aa8818Selim Cinek
154baa23274596246d03741457701ac515a73aa8818Selim Cinek    @Override
155baa23274596246d03741457701ac515a73aa8818Selim Cinek    protected void onDraw(Canvas canvas) {
156baa23274596246d03741457701ac515a73aa8818Selim Cinek        drawBackgroundCircle(canvas);
157baa23274596246d03741457701ac515a73aa8818Selim Cinek        drawArrow(canvas);
158baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.save();
159baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
160baa23274596246d03741457701ac515a73aa8818Selim Cinek        super.onDraw(canvas);
161baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.restore();
162baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
163baa23274596246d03741457701ac515a73aa8818Selim Cinek
164b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void setPreviewView(View v) {
165b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mPreviewView = v;
1660d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek        if (mPreviewView != null) {
1670d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek            mPreviewView.setVisibility(INVISIBLE);
1680d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek        }
169b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
170b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
171baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void drawArrow(Canvas canvas) {
172baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (mArrowAlpha > 0) {
173baa23274596246d03741457701ac515a73aa8818Selim Cinek            canvas.save();
174baa23274596246d03741457701ac515a73aa8818Selim Cinek            canvas.translate(mCenterX, mCenterY);
175baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (mIsLeft) {
176baa23274596246d03741457701ac515a73aa8818Selim Cinek                canvas.scale(-1.0f, 1.0f);
177baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
178baa23274596246d03741457701ac515a73aa8818Selim Cinek            canvas.translate(- mCircleRadius - mHintChevronPadding
179baa23274596246d03741457701ac515a73aa8818Selim Cinek                    - mArrowDrawable.getIntrinsicWidth() / 2,
180baa23274596246d03741457701ac515a73aa8818Selim Cinek                    - mArrowDrawable.getIntrinsicHeight() / 2);
181baa23274596246d03741457701ac515a73aa8818Selim Cinek            mArrowDrawable.setAlpha((int) (mArrowAlpha * 255));
182baa23274596246d03741457701ac515a73aa8818Selim Cinek            mArrowDrawable.draw(canvas);
183baa23274596246d03741457701ac515a73aa8818Selim Cinek            canvas.restore();
184baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
185baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
186baa23274596246d03741457701ac515a73aa8818Selim Cinek
187baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void updateIconColor() {
188baa23274596246d03741457701ac515a73aa8818Selim Cinek        Drawable drawable = getDrawable().mutate();
189baa23274596246d03741457701ac515a73aa8818Selim Cinek        float alpha = mCircleRadius / mMinBackgroundRadius;
190baa23274596246d03741457701ac515a73aa8818Selim Cinek        alpha = Math.min(1.0f, alpha);
191baa23274596246d03741457701ac515a73aa8818Selim Cinek        int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mInverseColor);
192baa23274596246d03741457701ac515a73aa8818Selim Cinek        drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
193baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
194baa23274596246d03741457701ac515a73aa8818Selim Cinek
195baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void drawBackgroundCircle(Canvas canvas) {
196baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (mCircleRadius > 0) {
197baa23274596246d03741457701ac515a73aa8818Selim Cinek            updateCircleColor();
198baa23274596246d03741457701ac515a73aa8818Selim Cinek            canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
199baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
200baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
201baa23274596246d03741457701ac515a73aa8818Selim Cinek
202baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void updateCircleColor() {
203baa23274596246d03741457701ac515a73aa8818Selim Cinek        float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
204baa23274596246d03741457701ac515a73aa8818Selim Cinek                (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
205b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (mPreviewView != null) {
206b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            float finishingFraction = 1 - Math.max(0, mCircleRadius - mCircleStartRadius)
207b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    / (mMaxCircleSize - mCircleStartRadius);
208b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            fraction *= finishingFraction;
209b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
210baa23274596246d03741457701ac515a73aa8818Selim Cinek        int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
211baa23274596246d03741457701ac515a73aa8818Selim Cinek                Color.red(mCircleColor),
212baa23274596246d03741457701ac515a73aa8818Selim Cinek                Color.green(mCircleColor), Color.blue(mCircleColor));
213baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setColor(color);
214baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
215baa23274596246d03741457701ac515a73aa8818Selim Cinek
216baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
217baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mCircleAnimator);
218b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        cancelAnimator(mPreviewClipper);
219b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mCircleStartRadius = mCircleRadius;
220baa23274596246d03741457701ac515a73aa8818Selim Cinek        float maxCircleSize = getMaxCircleSize();
221baa23274596246d03741457701ac515a73aa8818Selim Cinek        ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
222baa23274596246d03741457701ac515a73aa8818Selim Cinek        mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
223baa23274596246d03741457701ac515a73aa8818Selim Cinek                velocity, maxCircleSize);
224baa23274596246d03741457701ac515a73aa8818Selim Cinek        animatorToRadius.addListener(new AnimatorListenerAdapter() {
225baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
226baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationEnd(Animator animation) {
227baa23274596246d03741457701ac515a73aa8818Selim Cinek                mAnimationEndRunnable.run();
228baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
229baa23274596246d03741457701ac515a73aa8818Selim Cinek        });
230baa23274596246d03741457701ac515a73aa8818Selim Cinek        animatorToRadius.start();
231baa23274596246d03741457701ac515a73aa8818Selim Cinek        setImageAlpha(0, true);
232b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (mPreviewView != null) {
233b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewView.setVisibility(View.VISIBLE);
234b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewClipper = ViewAnimationUtils.createCircularReveal(
235b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
236b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    maxCircleSize);
237b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mFlingAnimationUtils.applyDismissing(mPreviewClipper, mCircleRadius, maxCircleSize,
238b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    velocity, maxCircleSize);
239b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewClipper.addListener(mClipEndListener);
240b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewClipper.start();
241b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
242baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
243baa23274596246d03741457701ac515a73aa8818Selim Cinek
244baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float getMaxCircleSize() {
245baa23274596246d03741457701ac515a73aa8818Selim Cinek        getLocationInWindow(mTempPoint);
246baa23274596246d03741457701ac515a73aa8818Selim Cinek        float rootWidth = getRootView().getWidth();
247baa23274596246d03741457701ac515a73aa8818Selim Cinek        float width = mTempPoint[0] + mCenterX;
248baa23274596246d03741457701ac515a73aa8818Selim Cinek        width = Math.max(rootWidth - width, width);
249baa23274596246d03741457701ac515a73aa8818Selim Cinek        float height = mTempPoint[1] + mCenterY;
250baa23274596246d03741457701ac515a73aa8818Selim Cinek        return (float) Math.hypot(width, height);
251baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
252baa23274596246d03741457701ac515a73aa8818Selim Cinek
253baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setCircleRadius(float circleRadius) {
2545386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, false, false);
2555386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    }
2565386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek
2575386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    public void setCircleRadius(float circleRadius, boolean slowAnimation) {
2585386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, slowAnimation, false);
259baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
260baa23274596246d03741457701ac515a73aa8818Selim Cinek
261baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setCircleRadiusWithoutAnimation(float circleRadius) {
262baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mCircleAnimator);
2635386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, false ,true);
264baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
265baa23274596246d03741457701ac515a73aa8818Selim Cinek
2665386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    private void setCircleRadius(float circleRadius, boolean slowAnimation, boolean noAnimation) {
267baa23274596246d03741457701ac515a73aa8818Selim Cinek
268baa23274596246d03741457701ac515a73aa8818Selim Cinek        // Check if we need a new animation
269baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden)
270baa23274596246d03741457701ac515a73aa8818Selim Cinek                || (mCircleAnimator == null && mCircleRadius == 0.0f);
271baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean nowHidden = circleRadius == 0.0f;
272baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
273baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!radiusNeedsAnimation) {
274baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (mCircleAnimator == null) {
275baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleRadius = circleRadius;
2760b75f83f142a4de5be77238c6030228e1b943aa2Jorim Jaggi                updateIconColor();
277baa23274596246d03741457701ac515a73aa8818Selim Cinek                invalidate();
278b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                if (nowHidden) {
279b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    if (mPreviewView != null) {
280b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView.setVisibility(View.INVISIBLE);
281b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    }
282b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                }
283baa23274596246d03741457701ac515a73aa8818Selim Cinek            } else if (!mCircleWillBeHidden) {
284baa23274596246d03741457701ac515a73aa8818Selim Cinek
285baa23274596246d03741457701ac515a73aa8818Selim Cinek                // We just update the end value
286baa23274596246d03741457701ac515a73aa8818Selim Cinek                float diff = circleRadius - mMinBackgroundRadius;
287baa23274596246d03741457701ac515a73aa8818Selim Cinek                PropertyValuesHolder[] values = mCircleAnimator.getValues();
288baa23274596246d03741457701ac515a73aa8818Selim Cinek                values[0].setFloatValues(mCircleStartValue + diff, circleRadius);
289baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
290baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
291baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
292baa23274596246d03741457701ac515a73aa8818Selim Cinek            cancelAnimator(mCircleAnimator);
293b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            cancelAnimator(mPreviewClipper);
294baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = getAnimatorToRadius(circleRadius);
295baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator = circleRadius == 0.0f
296baa23274596246d03741457701ac515a73aa8818Selim Cinek                    ? mDisappearInterpolator
297baa23274596246d03741457701ac515a73aa8818Selim Cinek                    : mAppearInterpolator;
298baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
2995386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            long duration = 250;
3005386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            if (!slowAnimation) {
3015386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                float durationFactor = Math.abs(mCircleRadius - circleRadius)
3025386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                        / (float) mMinBackgroundRadius;
3035386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
3045386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
3055386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            }
306baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
307baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
3080d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek            if (mPreviewView != null && mPreviewView.getVisibility() == View.VISIBLE) {
309b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewView.setVisibility(View.VISIBLE);
310b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper = ViewAnimationUtils.createCircularReveal(
311b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
312b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        circleRadius);
313b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.setInterpolator(interpolator);
314b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.setDuration(duration);
315b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.addListener(mClipEndListener);
316b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.addListener(new AnimatorListenerAdapter() {
317b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    @Override
318b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    public void onAnimationEnd(Animator animation) {
319b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView.setVisibility(View.INVISIBLE);
320b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    }
321b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                });
322b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.start();
323b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            }
324baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
325baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
326baa23274596246d03741457701ac515a73aa8818Selim Cinek
327baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator getAnimatorToRadius(float circleRadius) {
328baa23274596246d03741457701ac515a73aa8818Selim Cinek        ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius);
329baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleAnimator = animator;
330baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleStartValue = mCircleRadius;
331baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleWillBeHidden = circleRadius == 0.0f;
332baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
333baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
334baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
335baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleRadius = (float) animation.getAnimatedValue();
3360b75f83f142a4de5be77238c6030228e1b943aa2Jorim Jaggi                updateIconColor();
337baa23274596246d03741457701ac515a73aa8818Selim Cinek                invalidate();
338baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
339baa23274596246d03741457701ac515a73aa8818Selim Cinek        });
340baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addListener(mCircleEndListener);
341baa23274596246d03741457701ac515a73aa8818Selim Cinek        return animator;
342baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
343baa23274596246d03741457701ac515a73aa8818Selim Cinek
344baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void cancelAnimator(Animator animator) {
345baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (animator != null) {
346baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.cancel();
347baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
348baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
349baa23274596246d03741457701ac515a73aa8818Selim Cinek
350baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageScale(float imageScale, boolean animate) {
351baa23274596246d03741457701ac515a73aa8818Selim Cinek        setImageScale(imageScale, animate, -1, null);
352baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
353baa23274596246d03741457701ac515a73aa8818Selim Cinek
354baa23274596246d03741457701ac515a73aa8818Selim Cinek    /**
355baa23274596246d03741457701ac515a73aa8818Selim Cinek     * Sets the scale of the containing image
356baa23274596246d03741457701ac515a73aa8818Selim Cinek     *
357baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param imageScale The new Scale.
358baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param animate Should an animation be performed
359baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param duration If animate, whats the duration? When -1 we take the default duration
360baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param interpolator If animate, whats the interpolator? When null we take the default
361baa23274596246d03741457701ac515a73aa8818Selim Cinek     *                     interpolator.
362baa23274596246d03741457701ac515a73aa8818Selim Cinek     */
363baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageScale(float imageScale, boolean animate, long duration,
364baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator) {
365baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mScaleAnimator);
366baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!animate) {
367baa23274596246d03741457701ac515a73aa8818Selim Cinek            mImageScale = imageScale;
368baa23274596246d03741457701ac515a73aa8818Selim Cinek            invalidate();
369baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
370baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale);
371baa23274596246d03741457701ac515a73aa8818Selim Cinek            mScaleAnimator = animator;
372baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
373baa23274596246d03741457701ac515a73aa8818Selim Cinek                @Override
374baa23274596246d03741457701ac515a73aa8818Selim Cinek                public void onAnimationUpdate(ValueAnimator animation) {
375baa23274596246d03741457701ac515a73aa8818Selim Cinek                    mImageScale = (float) animation.getAnimatedValue();
376baa23274596246d03741457701ac515a73aa8818Selim Cinek                    invalidate();
377baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
378baa23274596246d03741457701ac515a73aa8818Selim Cinek            });
379baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addListener(mScaleEndListener);
380baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (interpolator == null) {
381baa23274596246d03741457701ac515a73aa8818Selim Cinek                interpolator = imageScale == 0.0f
382baa23274596246d03741457701ac515a73aa8818Selim Cinek                        ? mDisappearInterpolator
383baa23274596246d03741457701ac515a73aa8818Selim Cinek                        : mAppearInterpolator;
384baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
385baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
386baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (duration == -1) {
387baa23274596246d03741457701ac515a73aa8818Selim Cinek                float durationFactor = Math.abs(mImageScale - imageScale)
388baa23274596246d03741457701ac515a73aa8818Selim Cinek                        / (1.0f - MIN_ICON_SCALE_AMOUNT);
389baa23274596246d03741457701ac515a73aa8818Selim Cinek                durationFactor = Math.min(1.0f, durationFactor);
390baa23274596246d03741457701ac515a73aa8818Selim Cinek                duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
391baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
392baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
393baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
394baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
395baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
396baa23274596246d03741457701ac515a73aa8818Selim Cinek
397baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageAlpha(float alpha, boolean animate) {
398baa23274596246d03741457701ac515a73aa8818Selim Cinek        setImageAlpha(alpha, animate, -1, null, null);
399baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
400baa23274596246d03741457701ac515a73aa8818Selim Cinek
401baa23274596246d03741457701ac515a73aa8818Selim Cinek    /**
402baa23274596246d03741457701ac515a73aa8818Selim Cinek     * Sets the alpha of the containing image
403baa23274596246d03741457701ac515a73aa8818Selim Cinek     *
404baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param alpha The new alpha.
405baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param animate Should an animation be performed
406baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param duration If animate, whats the duration? When -1 we take the default duration
407baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param interpolator If animate, whats the interpolator? When null we take the default
408baa23274596246d03741457701ac515a73aa8818Selim Cinek     *                     interpolator.
409baa23274596246d03741457701ac515a73aa8818Selim Cinek     */
410baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageAlpha(float alpha, boolean animate, long duration,
411baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator, Runnable runnable) {
412baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mAlphaAnimator);
413baa23274596246d03741457701ac515a73aa8818Selim Cinek        int endAlpha = (int) (alpha * 255);
4144ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos        final Drawable background = getBackground();
415baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!animate) {
4164ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos            if (background != null) background.mutate().setAlpha(endAlpha);
417baa23274596246d03741457701ac515a73aa8818Selim Cinek            setImageAlpha(endAlpha);
418baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
419baa23274596246d03741457701ac515a73aa8818Selim Cinek            int currentAlpha = getImageAlpha();
420baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
421baa23274596246d03741457701ac515a73aa8818Selim Cinek            mAlphaAnimator = animator;
422baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
423baa23274596246d03741457701ac515a73aa8818Selim Cinek                @Override
424baa23274596246d03741457701ac515a73aa8818Selim Cinek                public void onAnimationUpdate(ValueAnimator animation) {
4254ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    int alpha = (int) animation.getAnimatedValue();
4264ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    if (background != null) background.mutate().setAlpha(alpha);
4274ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    setImageAlpha(alpha);
428baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
429baa23274596246d03741457701ac515a73aa8818Selim Cinek            });
430baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addListener(mAlphaEndListener);
431baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (interpolator == null) {
432baa23274596246d03741457701ac515a73aa8818Selim Cinek                interpolator = alpha == 0.0f
433baa23274596246d03741457701ac515a73aa8818Selim Cinek                        ? mDisappearInterpolator
434baa23274596246d03741457701ac515a73aa8818Selim Cinek                        : mAppearInterpolator;
435baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
436baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
437baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (duration == -1) {
438baa23274596246d03741457701ac515a73aa8818Selim Cinek                float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
439baa23274596246d03741457701ac515a73aa8818Selim Cinek                durationFactor = Math.min(1.0f, durationFactor);
440baa23274596246d03741457701ac515a73aa8818Selim Cinek                duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
441baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
442baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
443baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (runnable != null) {
444baa23274596246d03741457701ac515a73aa8818Selim Cinek                animator.addListener(getEndListener(runnable));
445baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
446baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
447baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
448baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
449baa23274596246d03741457701ac515a73aa8818Selim Cinek
450baa23274596246d03741457701ac515a73aa8818Selim Cinek    private Animator.AnimatorListener getEndListener(final Runnable runnable) {
451baa23274596246d03741457701ac515a73aa8818Selim Cinek        return new AnimatorListenerAdapter() {
452baa23274596246d03741457701ac515a73aa8818Selim Cinek            boolean mCancelled;
453baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
454baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationCancel(Animator animation) {
455baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCancelled = true;
456baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
457baa23274596246d03741457701ac515a73aa8818Selim Cinek
458baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
459baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationEnd(Animator animation) {
460baa23274596246d03741457701ac515a73aa8818Selim Cinek                if (!mCancelled) {
461baa23274596246d03741457701ac515a73aa8818Selim Cinek                    runnable.run();
462baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
463baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
464baa23274596246d03741457701ac515a73aa8818Selim Cinek        };
465baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
466baa23274596246d03741457701ac515a73aa8818Selim Cinek
467baa23274596246d03741457701ac515a73aa8818Selim Cinek    public float getCircleRadius() {
468baa23274596246d03741457701ac515a73aa8818Selim Cinek        return mCircleRadius;
469baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
470baa23274596246d03741457701ac515a73aa8818Selim Cinek
471baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void showArrow(boolean show) {
472baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mArrowAnimator);
473baa23274596246d03741457701ac515a73aa8818Selim Cinek        float targetAlpha = show ? 1.0f : 0.0f;
474baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (mArrowAlpha == targetAlpha) {
475baa23274596246d03741457701ac515a73aa8818Selim Cinek            return;
476baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
477baa23274596246d03741457701ac515a73aa8818Selim Cinek        ValueAnimator animator = ValueAnimator.ofFloat(mArrowAlpha, targetAlpha);
478baa23274596246d03741457701ac515a73aa8818Selim Cinek        mArrowAnimator = animator;
479baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
480baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
481baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
482baa23274596246d03741457701ac515a73aa8818Selim Cinek                mArrowAlpha = (float) animation.getAnimatedValue();
483baa23274596246d03741457701ac515a73aa8818Selim Cinek                invalidate();
484baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
485baa23274596246d03741457701ac515a73aa8818Selim Cinek        });
486baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addListener(mArrowEndListener);
487baa23274596246d03741457701ac515a73aa8818Selim Cinek        Interpolator interpolator = show
488baa23274596246d03741457701ac515a73aa8818Selim Cinek                    ? mAppearInterpolator
489baa23274596246d03741457701ac515a73aa8818Selim Cinek                    : mDisappearInterpolator;
490baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.setInterpolator(interpolator);
491baa23274596246d03741457701ac515a73aa8818Selim Cinek        float durationFactor = Math.abs(mArrowAlpha - targetAlpha);
492baa23274596246d03741457701ac515a73aa8818Selim Cinek        long duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
493baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.setDuration(duration);
494baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.start();
495baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
496baa23274596246d03741457701ac515a73aa8818Selim Cinek
497baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setIsLeft(boolean left) {
498baa23274596246d03741457701ac515a73aa8818Selim Cinek        mIsLeft = left;
499baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
50038fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos
50138fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    @Override
50238fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    public boolean performClick() {
50338fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        if (isClickable()) {
50438fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos            return super.performClick();
50538fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        } else {
50638fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos            return false;
50738fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        }
50838fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    }
509baa23274596246d03741457701ac515a73aa8818Selim Cinek}
510