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;
2672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinekimport android.graphics.CanvasProperty;
27baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.Color;
28baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.Paint;
29baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.PorterDuff;
30baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.graphics.drawable.Drawable;
31baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.util.AttributeSet;
3272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinekimport android.view.DisplayListCanvas;
3372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinekimport android.view.RenderNodeAnimator;
34b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.View;
35b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.ViewAnimationUtils;
36baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.view.animation.Interpolator;
37baa23274596246d03741457701ac515a73aa8818Selim Cinekimport android.widget.ImageView;
38372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek
39c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators;
40baa23274596246d03741457701ac515a73aa8818Selim Cinekimport com.android.systemui.R;
4127c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggiimport com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
42baa23274596246d03741457701ac515a73aa8818Selim Cinek
43baa23274596246d03741457701ac515a73aa8818Selim Cinek/**
44baa23274596246d03741457701ac515a73aa8818Selim Cinek * An ImageView which does not have overlapping renderings commands and therefore does not need a
45baa23274596246d03741457701ac515a73aa8818Selim Cinek * layer when alpha is changed.
46baa23274596246d03741457701ac515a73aa8818Selim Cinek */
47baa23274596246d03741457701ac515a73aa8818Selim Cinekpublic class KeyguardAffordanceView extends ImageView {
48baa23274596246d03741457701ac515a73aa8818Selim Cinek
49baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long CIRCLE_APPEAR_DURATION = 80;
50baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
51baa23274596246d03741457701ac515a73aa8818Selim Cinek    private static final long NORMAL_ANIMATION_DURATION = 200;
52baa23274596246d03741457701ac515a73aa8818Selim Cinek    public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
53baa23274596246d03741457701ac515a73aa8818Selim Cinek    public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
54baa23274596246d03741457701ac515a73aa8818Selim Cinek
55baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mMinBackgroundRadius;
56baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final Paint mCirclePaint;
57baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mInverseColor;
58baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final int mNormalColor;
59baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final ArgbEvaluator mColorInterpolator;
60baa23274596246d03741457701ac515a73aa8818Selim Cinek    private final FlingAnimationUtils mFlingAnimationUtils;
61baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mCircleRadius;
62baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCenterX;
63baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCenterY;
64baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mCircleAnimator;
65baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mAlphaAnimator;
66baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator mScaleAnimator;
67baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float mCircleStartValue;
68baa23274596246d03741457701ac515a73aa8818Selim Cinek    private boolean mCircleWillBeHidden;
69baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int[] mTempPoint = new int[2];
70a4eba9fc35165dc3391fb1b9580341bba7928277Adrian Roos    private float mImageScale = 1f;
71baa23274596246d03741457701ac515a73aa8818Selim Cinek    private int mCircleColor;
72baa23274596246d03741457701ac515a73aa8818Selim Cinek    private boolean mIsLeft;
73b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private View mPreviewView;
74b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private float mCircleStartRadius;
75b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private float mMaxCircleSize;
76b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private Animator mPreviewClipper;
7727c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi    private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
7872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private boolean mSupportHardware;
7972bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private boolean mFinishing;
80372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    private boolean mLaunchingAffordance;
8172bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek
8272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private CanvasProperty<Float> mHwCircleRadius;
8372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private CanvasProperty<Float> mHwCenterX;
8472bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private CanvasProperty<Float> mHwCenterY;
8572bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private CanvasProperty<Paint> mHwCirclePaint;
8672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek
87b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
88b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        @Override
89b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        public void onAnimationEnd(Animator animation) {
90b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            mPreviewClipper = null;
91b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
92b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    };
93baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
94baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
95baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
96baa23274596246d03741457701ac515a73aa8818Selim Cinek            mCircleAnimator = null;
97baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
98baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
99baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() {
100baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
101baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
102baa23274596246d03741457701ac515a73aa8818Selim Cinek            mScaleAnimator = null;
103baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
104baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
105baa23274596246d03741457701ac515a73aa8818Selim Cinek    private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() {
106baa23274596246d03741457701ac515a73aa8818Selim Cinek        @Override
107baa23274596246d03741457701ac515a73aa8818Selim Cinek        public void onAnimationEnd(Animator animation) {
108baa23274596246d03741457701ac515a73aa8818Selim Cinek            mAlphaAnimator = null;
109baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
110baa23274596246d03741457701ac515a73aa8818Selim Cinek    };
111baa23274596246d03741457701ac515a73aa8818Selim Cinek
112baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context) {
113baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, null);
114baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
115baa23274596246d03741457701ac515a73aa8818Selim Cinek
116baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs) {
117baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, attrs, 0);
118baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
119baa23274596246d03741457701ac515a73aa8818Selim Cinek
120baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
121baa23274596246d03741457701ac515a73aa8818Selim Cinek        this(context, attrs, defStyleAttr, 0);
122baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
123baa23274596246d03741457701ac515a73aa8818Selim Cinek
124baa23274596246d03741457701ac515a73aa8818Selim Cinek    public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr,
125baa23274596246d03741457701ac515a73aa8818Selim Cinek            int defStyleRes) {
126baa23274596246d03741457701ac515a73aa8818Selim Cinek        super(context, attrs, defStyleAttr, defStyleRes);
127baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint = new Paint();
128baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setAntiAlias(true);
129baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleColor = 0xffffffff;
130baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setColor(mCircleColor);
131baa23274596246d03741457701ac515a73aa8818Selim Cinek
132baa23274596246d03741457701ac515a73aa8818Selim Cinek        mNormalColor = 0xffffffff;
133baa23274596246d03741457701ac515a73aa8818Selim Cinek        mInverseColor = 0xff000000;
134baa23274596246d03741457701ac515a73aa8818Selim Cinek        mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
135baa23274596246d03741457701ac515a73aa8818Selim Cinek                R.dimen.keyguard_affordance_min_background_radius);
136baa23274596246d03741457701ac515a73aa8818Selim Cinek        mColorInterpolator = new ArgbEvaluator();
137baa23274596246d03741457701ac515a73aa8818Selim Cinek        mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
138baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
139baa23274596246d03741457701ac515a73aa8818Selim Cinek
140baa23274596246d03741457701ac515a73aa8818Selim Cinek    @Override
141baa23274596246d03741457701ac515a73aa8818Selim Cinek    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
142baa23274596246d03741457701ac515a73aa8818Selim Cinek        super.onLayout(changed, left, top, right, bottom);
143baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCenterX = getWidth() / 2;
144baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCenterY = getHeight() / 2;
145b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mMaxCircleSize = getMaxCircleSize();
146baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
147baa23274596246d03741457701ac515a73aa8818Selim Cinek
148baa23274596246d03741457701ac515a73aa8818Selim Cinek    @Override
149baa23274596246d03741457701ac515a73aa8818Selim Cinek    protected void onDraw(Canvas canvas) {
1504ae263c87c0a55613d34738aa2b44ccec4eb75ffSelim Cinek        mSupportHardware = canvas.isHardwareAccelerated();
151baa23274596246d03741457701ac515a73aa8818Selim Cinek        drawBackgroundCircle(canvas);
152baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.save();
153baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
154baa23274596246d03741457701ac515a73aa8818Selim Cinek        super.onDraw(canvas);
155baa23274596246d03741457701ac515a73aa8818Selim Cinek        canvas.restore();
156baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
157baa23274596246d03741457701ac515a73aa8818Selim Cinek
158b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void setPreviewView(View v) {
159372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        View oldPreviewView = mPreviewView;
160b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mPreviewView = v;
1610d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek        if (mPreviewView != null) {
162372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            mPreviewView.setVisibility(mLaunchingAffordance
163372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek                    ? oldPreviewView.getVisibility() : INVISIBLE);
1640d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek        }
165b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
166b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
167baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void updateIconColor() {
168baa23274596246d03741457701ac515a73aa8818Selim Cinek        Drawable drawable = getDrawable().mutate();
169baa23274596246d03741457701ac515a73aa8818Selim Cinek        float alpha = mCircleRadius / mMinBackgroundRadius;
170baa23274596246d03741457701ac515a73aa8818Selim Cinek        alpha = Math.min(1.0f, alpha);
171baa23274596246d03741457701ac515a73aa8818Selim Cinek        int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mInverseColor);
172baa23274596246d03741457701ac515a73aa8818Selim Cinek        drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
173baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
174baa23274596246d03741457701ac515a73aa8818Selim Cinek
175baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void drawBackgroundCircle(Canvas canvas) {
176372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        if (mCircleRadius > 0 || mFinishing) {
1772c984f16eb4afc3455482cfbf3e8b03c97189bcaSelim Cinek            if (mFinishing && mSupportHardware && mHwCenterX != null) {
1782c984f16eb4afc3455482cfbf3e8b03c97189bcaSelim Cinek                // Our hardware drawing proparties can be null if the finishing started but we have
1792c984f16eb4afc3455482cfbf3e8b03c97189bcaSelim Cinek                // never drawn before. In that case we are not doing a render thread animation
1802c984f16eb4afc3455482cfbf3e8b03c97189bcaSelim Cinek                // anyway, so we need to use the normal drawing.
18172bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
18272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
18372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                        mHwCirclePaint);
18472bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            } else {
18572bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                updateCircleColor();
18672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
18772bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            }
188baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
189baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
190baa23274596246d03741457701ac515a73aa8818Selim Cinek
191baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void updateCircleColor() {
192baa23274596246d03741457701ac515a73aa8818Selim Cinek        float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
193baa23274596246d03741457701ac515a73aa8818Selim Cinek                (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
1946746c28b812446e847fbfec0f54025558584e70aSelim Cinek        if (mPreviewView != null && mPreviewView.getVisibility() == VISIBLE) {
195b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            float finishingFraction = 1 - Math.max(0, mCircleRadius - mCircleStartRadius)
196b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    / (mMaxCircleSize - mCircleStartRadius);
197b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            fraction *= finishingFraction;
198b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
199baa23274596246d03741457701ac515a73aa8818Selim Cinek        int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
200baa23274596246d03741457701ac515a73aa8818Selim Cinek                Color.red(mCircleColor),
201baa23274596246d03741457701ac515a73aa8818Selim Cinek                Color.green(mCircleColor), Color.blue(mCircleColor));
202baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCirclePaint.setColor(color);
203baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
204baa23274596246d03741457701ac515a73aa8818Selim Cinek
205baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
206baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mCircleAnimator);
207b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        cancelAnimator(mPreviewClipper);
20872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        mFinishing = true;
209b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mCircleStartRadius = mCircleRadius;
210372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        final float maxCircleSize = getMaxCircleSize();
21172bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        Animator animatorToRadius;
21272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        if (mSupportHardware) {
21372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            initHwProperties();
21472bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
215372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            startRtAlphaFadeIn();
21672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        } else {
21772bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            animatorToRadius = getAnimatorToRadius(maxCircleSize);
21872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        }
219baa23274596246d03741457701ac515a73aa8818Selim Cinek        mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
220baa23274596246d03741457701ac515a73aa8818Selim Cinek                velocity, maxCircleSize);
221baa23274596246d03741457701ac515a73aa8818Selim Cinek        animatorToRadius.addListener(new AnimatorListenerAdapter() {
222baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
223baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationEnd(Animator animation) {
224baa23274596246d03741457701ac515a73aa8818Selim Cinek                mAnimationEndRunnable.run();
22572bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                mFinishing = false;
226372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek                mCircleRadius = maxCircleSize;
227372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek                invalidate();
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();
24172bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            if (mSupportHardware) {
24272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                startRtCircleFadeOut(animatorToRadius.getDuration());
24372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek            }
244b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
245baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
246baa23274596246d03741457701ac515a73aa8818Selim Cinek
247372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    /**
248372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek     * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
249372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek     * alpha 0 in the beginning.
250372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek     */
251372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    private void startRtAlphaFadeIn() {
252372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        if (mCircleRadius == 0 && mPreviewView == null) {
253372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            Paint modifiedPaint = new Paint(mCirclePaint);
254372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            modifiedPaint.setColor(mCircleColor);
255372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            modifiedPaint.setAlpha(0);
256372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
257372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
258372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek                    RenderNodeAnimator.PAINT_ALPHA, 255);
259372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            animator.setTarget(this);
260c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek            animator.setInterpolator(Interpolators.ALPHA_IN);
261372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            animator.setDuration(250);
262372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            animator.start();
263372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        }
264372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    }
265372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek
266372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    public void instantFinishAnimation() {
267372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        cancelAnimator(mPreviewClipper);
268372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        if (mPreviewView != null) {
269372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            mPreviewView.setClipBounds(null);
270372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek            mPreviewView.setVisibility(View.VISIBLE);
271372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        }
272372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        mCircleRadius = getMaxCircleSize();
273372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        setImageAlpha(0, false);
274372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        invalidate();
275372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    }
276372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek
27772bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private void startRtCircleFadeOut(long duration) {
27872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
27972bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek                RenderNodeAnimator.PAINT_ALPHA, 0);
28072bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        animator.setDuration(duration);
281c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        animator.setInterpolator(Interpolators.ALPHA_OUT);
28272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        animator.setTarget(this);
28372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        animator.start();
28472bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    }
28572bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek
28672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private Animator getRtAnimatorToRadius(float circleRadius) {
28772bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        RenderNodeAnimator animator = new RenderNodeAnimator(mHwCircleRadius, circleRadius);
28872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        animator.setTarget(this);
28972bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        return animator;
29072bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    }
29172bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek
29272bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    private void initHwProperties() {
29372bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        mHwCenterX = CanvasProperty.createFloat(mCenterX);
29472bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        mHwCenterY = CanvasProperty.createFloat(mCenterY);
29572bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        mHwCirclePaint = CanvasProperty.createPaint(mCirclePaint);
29672bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek        mHwCircleRadius = CanvasProperty.createFloat(mCircleRadius);
29772bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek    }
29872bcaa20afab21eedf749c7a4bdb807dd41b06f8Selim Cinek
299baa23274596246d03741457701ac515a73aa8818Selim Cinek    private float getMaxCircleSize() {
300baa23274596246d03741457701ac515a73aa8818Selim Cinek        getLocationInWindow(mTempPoint);
301baa23274596246d03741457701ac515a73aa8818Selim Cinek        float rootWidth = getRootView().getWidth();
302baa23274596246d03741457701ac515a73aa8818Selim Cinek        float width = mTempPoint[0] + mCenterX;
303baa23274596246d03741457701ac515a73aa8818Selim Cinek        width = Math.max(rootWidth - width, width);
304baa23274596246d03741457701ac515a73aa8818Selim Cinek        float height = mTempPoint[1] + mCenterY;
305baa23274596246d03741457701ac515a73aa8818Selim Cinek        return (float) Math.hypot(width, height);
306baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
307baa23274596246d03741457701ac515a73aa8818Selim Cinek
308baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setCircleRadius(float circleRadius) {
3095386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, false, false);
3105386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    }
3115386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek
3125386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    public void setCircleRadius(float circleRadius, boolean slowAnimation) {
3135386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, slowAnimation, false);
314baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
315baa23274596246d03741457701ac515a73aa8818Selim Cinek
316baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setCircleRadiusWithoutAnimation(float circleRadius) {
317baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mCircleAnimator);
3185386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek        setCircleRadius(circleRadius, false ,true);
319baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
320baa23274596246d03741457701ac515a73aa8818Selim Cinek
3215386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek    private void setCircleRadius(float circleRadius, boolean slowAnimation, boolean noAnimation) {
322baa23274596246d03741457701ac515a73aa8818Selim Cinek
323baa23274596246d03741457701ac515a73aa8818Selim Cinek        // Check if we need a new animation
324baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden)
325baa23274596246d03741457701ac515a73aa8818Selim Cinek                || (mCircleAnimator == null && mCircleRadius == 0.0f);
326baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean nowHidden = circleRadius == 0.0f;
327baa23274596246d03741457701ac515a73aa8818Selim Cinek        boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
328baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!radiusNeedsAnimation) {
329baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (mCircleAnimator == null) {
330baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleRadius = circleRadius;
3310b75f83f142a4de5be77238c6030228e1b943aa2Jorim Jaggi                updateIconColor();
332baa23274596246d03741457701ac515a73aa8818Selim Cinek                invalidate();
333b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                if (nowHidden) {
334b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    if (mPreviewView != null) {
335b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView.setVisibility(View.INVISIBLE);
336b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    }
337b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                }
338baa23274596246d03741457701ac515a73aa8818Selim Cinek            } else if (!mCircleWillBeHidden) {
339baa23274596246d03741457701ac515a73aa8818Selim Cinek
340baa23274596246d03741457701ac515a73aa8818Selim Cinek                // We just update the end value
341baa23274596246d03741457701ac515a73aa8818Selim Cinek                float diff = circleRadius - mMinBackgroundRadius;
342baa23274596246d03741457701ac515a73aa8818Selim Cinek                PropertyValuesHolder[] values = mCircleAnimator.getValues();
343baa23274596246d03741457701ac515a73aa8818Selim Cinek                values[0].setFloatValues(mCircleStartValue + diff, circleRadius);
344baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
345baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
346baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
347baa23274596246d03741457701ac515a73aa8818Selim Cinek            cancelAnimator(mCircleAnimator);
348b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            cancelAnimator(mPreviewClipper);
349baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = getAnimatorToRadius(circleRadius);
350baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator = circleRadius == 0.0f
351c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                    ? Interpolators.FAST_OUT_LINEAR_IN
352c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                    : Interpolators.LINEAR_OUT_SLOW_IN;
353baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
3545386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            long duration = 250;
3555386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            if (!slowAnimation) {
3565386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                float durationFactor = Math.abs(mCircleRadius - circleRadius)
3575386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                        / (float) mMinBackgroundRadius;
3585386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
3595386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek                duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
3605386fb337d3f2bd9b0ea673b5f60483246e5d0cdSelim Cinek            }
361baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
362baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
3630d61b51852278de763d971ae0c57b27e6ed1ce6aSelim Cinek            if (mPreviewView != null && mPreviewView.getVisibility() == View.VISIBLE) {
364b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewView.setVisibility(View.VISIBLE);
365b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper = ViewAnimationUtils.createCircularReveal(
366b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView, getLeft() + mCenterX, getTop() + mCenterY, mCircleRadius,
367b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        circleRadius);
368b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.setInterpolator(interpolator);
369b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.setDuration(duration);
370b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.addListener(mClipEndListener);
371b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.addListener(new AnimatorListenerAdapter() {
372b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    @Override
373b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    public void onAnimationEnd(Animator animation) {
374b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                        mPreviewView.setVisibility(View.INVISIBLE);
375b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    }
376b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                });
377b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                mPreviewClipper.start();
378b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            }
379baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
380baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
381baa23274596246d03741457701ac515a73aa8818Selim Cinek
382baa23274596246d03741457701ac515a73aa8818Selim Cinek    private ValueAnimator getAnimatorToRadius(float circleRadius) {
383baa23274596246d03741457701ac515a73aa8818Selim Cinek        ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius);
384baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleAnimator = animator;
385baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleStartValue = mCircleRadius;
386baa23274596246d03741457701ac515a73aa8818Selim Cinek        mCircleWillBeHidden = circleRadius == 0.0f;
387baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
388baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
389baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
390baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCircleRadius = (float) animation.getAnimatedValue();
3910b75f83f142a4de5be77238c6030228e1b943aa2Jorim Jaggi                updateIconColor();
392baa23274596246d03741457701ac515a73aa8818Selim Cinek                invalidate();
393baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
394baa23274596246d03741457701ac515a73aa8818Selim Cinek        });
395baa23274596246d03741457701ac515a73aa8818Selim Cinek        animator.addListener(mCircleEndListener);
396baa23274596246d03741457701ac515a73aa8818Selim Cinek        return animator;
397baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
398baa23274596246d03741457701ac515a73aa8818Selim Cinek
399baa23274596246d03741457701ac515a73aa8818Selim Cinek    private void cancelAnimator(Animator animator) {
400baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (animator != null) {
401baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.cancel();
402baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
403baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
404baa23274596246d03741457701ac515a73aa8818Selim Cinek
405baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageScale(float imageScale, boolean animate) {
406baa23274596246d03741457701ac515a73aa8818Selim Cinek        setImageScale(imageScale, animate, -1, null);
407baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
408baa23274596246d03741457701ac515a73aa8818Selim Cinek
409baa23274596246d03741457701ac515a73aa8818Selim Cinek    /**
410baa23274596246d03741457701ac515a73aa8818Selim Cinek     * Sets the scale of the containing image
411baa23274596246d03741457701ac515a73aa8818Selim Cinek     *
412baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param imageScale The new Scale.
413baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param animate Should an animation be performed
414baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param duration If animate, whats the duration? When -1 we take the default duration
415baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param interpolator If animate, whats the interpolator? When null we take the default
416baa23274596246d03741457701ac515a73aa8818Selim Cinek     *                     interpolator.
417baa23274596246d03741457701ac515a73aa8818Selim Cinek     */
418baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageScale(float imageScale, boolean animate, long duration,
419baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator) {
420baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mScaleAnimator);
421baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!animate) {
422baa23274596246d03741457701ac515a73aa8818Selim Cinek            mImageScale = imageScale;
423baa23274596246d03741457701ac515a73aa8818Selim Cinek            invalidate();
424baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
425baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale);
426baa23274596246d03741457701ac515a73aa8818Selim Cinek            mScaleAnimator = animator;
427baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
428baa23274596246d03741457701ac515a73aa8818Selim Cinek                @Override
429baa23274596246d03741457701ac515a73aa8818Selim Cinek                public void onAnimationUpdate(ValueAnimator animation) {
430baa23274596246d03741457701ac515a73aa8818Selim Cinek                    mImageScale = (float) animation.getAnimatedValue();
431baa23274596246d03741457701ac515a73aa8818Selim Cinek                    invalidate();
432baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
433baa23274596246d03741457701ac515a73aa8818Selim Cinek            });
434baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addListener(mScaleEndListener);
435baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (interpolator == null) {
436baa23274596246d03741457701ac515a73aa8818Selim Cinek                interpolator = imageScale == 0.0f
437c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                        ? Interpolators.FAST_OUT_LINEAR_IN
438c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                        : Interpolators.LINEAR_OUT_SLOW_IN;
439baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
440baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
441baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (duration == -1) {
442baa23274596246d03741457701ac515a73aa8818Selim Cinek                float durationFactor = Math.abs(mImageScale - imageScale)
443baa23274596246d03741457701ac515a73aa8818Selim Cinek                        / (1.0f - MIN_ICON_SCALE_AMOUNT);
444baa23274596246d03741457701ac515a73aa8818Selim Cinek                durationFactor = Math.min(1.0f, durationFactor);
445baa23274596246d03741457701ac515a73aa8818Selim Cinek                duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
446baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
447baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
448baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
449baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
450baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
451baa23274596246d03741457701ac515a73aa8818Selim Cinek
45227c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi    public void setRestingAlpha(float alpha) {
45327c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi        mRestingAlpha = alpha;
45427c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi
45527c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi        // TODO: Handle the case an animation is playing.
45627c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi        setImageAlpha(alpha, false);
45727c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi    }
45827c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi
45927c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi    public float getRestingAlpha() {
46027c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi        return mRestingAlpha;
46127c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi    }
46227c9b7435083c5620bddefa0cce6a056863b4801Jorim Jaggi
463baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageAlpha(float alpha, boolean animate) {
464baa23274596246d03741457701ac515a73aa8818Selim Cinek        setImageAlpha(alpha, animate, -1, null, null);
465baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
466baa23274596246d03741457701ac515a73aa8818Selim Cinek
467baa23274596246d03741457701ac515a73aa8818Selim Cinek    /**
468baa23274596246d03741457701ac515a73aa8818Selim Cinek     * Sets the alpha of the containing image
469baa23274596246d03741457701ac515a73aa8818Selim Cinek     *
470baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param alpha The new alpha.
471baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param animate Should an animation be performed
472baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param duration If animate, whats the duration? When -1 we take the default duration
473baa23274596246d03741457701ac515a73aa8818Selim Cinek     * @param interpolator If animate, whats the interpolator? When null we take the default
474baa23274596246d03741457701ac515a73aa8818Selim Cinek     *                     interpolator.
475baa23274596246d03741457701ac515a73aa8818Selim Cinek     */
476baa23274596246d03741457701ac515a73aa8818Selim Cinek    public void setImageAlpha(float alpha, boolean animate, long duration,
477baa23274596246d03741457701ac515a73aa8818Selim Cinek            Interpolator interpolator, Runnable runnable) {
478baa23274596246d03741457701ac515a73aa8818Selim Cinek        cancelAnimator(mAlphaAnimator);
479372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        alpha = mLaunchingAffordance ? 0 : alpha;
480baa23274596246d03741457701ac515a73aa8818Selim Cinek        int endAlpha = (int) (alpha * 255);
4814ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos        final Drawable background = getBackground();
482baa23274596246d03741457701ac515a73aa8818Selim Cinek        if (!animate) {
4834ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos            if (background != null) background.mutate().setAlpha(endAlpha);
484baa23274596246d03741457701ac515a73aa8818Selim Cinek            setImageAlpha(endAlpha);
485baa23274596246d03741457701ac515a73aa8818Selim Cinek        } else {
486baa23274596246d03741457701ac515a73aa8818Selim Cinek            int currentAlpha = getImageAlpha();
487baa23274596246d03741457701ac515a73aa8818Selim Cinek            ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
488baa23274596246d03741457701ac515a73aa8818Selim Cinek            mAlphaAnimator = animator;
489baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
490baa23274596246d03741457701ac515a73aa8818Selim Cinek                @Override
491baa23274596246d03741457701ac515a73aa8818Selim Cinek                public void onAnimationUpdate(ValueAnimator animation) {
4924ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    int alpha = (int) animation.getAnimatedValue();
4934ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    if (background != null) background.mutate().setAlpha(alpha);
4944ebcdfdd4294cc52a68fb150bc7a34f005290ea3Adrian Roos                    setImageAlpha(alpha);
495baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
496baa23274596246d03741457701ac515a73aa8818Selim Cinek            });
497baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.addListener(mAlphaEndListener);
498baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (interpolator == null) {
499baa23274596246d03741457701ac515a73aa8818Selim Cinek                interpolator = alpha == 0.0f
500c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                        ? Interpolators.FAST_OUT_LINEAR_IN
501c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                        : Interpolators.LINEAR_OUT_SLOW_IN;
502baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
503baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setInterpolator(interpolator);
504baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (duration == -1) {
505baa23274596246d03741457701ac515a73aa8818Selim Cinek                float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
506baa23274596246d03741457701ac515a73aa8818Selim Cinek                durationFactor = Math.min(1.0f, durationFactor);
507baa23274596246d03741457701ac515a73aa8818Selim Cinek                duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
508baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
509baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.setDuration(duration);
510baa23274596246d03741457701ac515a73aa8818Selim Cinek            if (runnable != null) {
511baa23274596246d03741457701ac515a73aa8818Selim Cinek                animator.addListener(getEndListener(runnable));
512baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
513baa23274596246d03741457701ac515a73aa8818Selim Cinek            animator.start();
514baa23274596246d03741457701ac515a73aa8818Selim Cinek        }
515baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
516baa23274596246d03741457701ac515a73aa8818Selim Cinek
517baa23274596246d03741457701ac515a73aa8818Selim Cinek    private Animator.AnimatorListener getEndListener(final Runnable runnable) {
518baa23274596246d03741457701ac515a73aa8818Selim Cinek        return new AnimatorListenerAdapter() {
519baa23274596246d03741457701ac515a73aa8818Selim Cinek            boolean mCancelled;
520baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
521baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationCancel(Animator animation) {
522baa23274596246d03741457701ac515a73aa8818Selim Cinek                mCancelled = true;
523baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
524baa23274596246d03741457701ac515a73aa8818Selim Cinek
525baa23274596246d03741457701ac515a73aa8818Selim Cinek            @Override
526baa23274596246d03741457701ac515a73aa8818Selim Cinek            public void onAnimationEnd(Animator animation) {
527baa23274596246d03741457701ac515a73aa8818Selim Cinek                if (!mCancelled) {
528baa23274596246d03741457701ac515a73aa8818Selim Cinek                    runnable.run();
529baa23274596246d03741457701ac515a73aa8818Selim Cinek                }
530baa23274596246d03741457701ac515a73aa8818Selim Cinek            }
531baa23274596246d03741457701ac515a73aa8818Selim Cinek        };
532baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
533baa23274596246d03741457701ac515a73aa8818Selim Cinek
534baa23274596246d03741457701ac515a73aa8818Selim Cinek    public float getCircleRadius() {
535baa23274596246d03741457701ac515a73aa8818Selim Cinek        return mCircleRadius;
536baa23274596246d03741457701ac515a73aa8818Selim Cinek    }
537baa23274596246d03741457701ac515a73aa8818Selim Cinek
53838fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    @Override
53938fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    public boolean performClick() {
54038fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        if (isClickable()) {
54138fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos            return super.performClick();
54238fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        } else {
54338fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos            return false;
54438fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos        }
54538fc7606b1d8869cd79a19f324bbcd97ba320994Adrian Roos    }
546372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek
547372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    public void setLaunchingAffordance(boolean launchingAffordance) {
548372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek        mLaunchingAffordance = launchingAffordance;
549372d1bdd54fe977c71bdaae895687c76ac35202aSelim Cinek    }
550baa23274596246d03741457701ac515a73aa8818Selim Cinek}
551