1223622a50db319d634616311ff74267cf49679e7Alan Viverette/*
2223622a50db319d634616311ff74267cf49679e7Alan Viverette * Copyright (C) 2013 The Android Open Source Project
3223622a50db319d634616311ff74267cf49679e7Alan Viverette *
4223622a50db319d634616311ff74267cf49679e7Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5223622a50db319d634616311ff74267cf49679e7Alan Viverette * you may not use this file except in compliance with the License.
6223622a50db319d634616311ff74267cf49679e7Alan Viverette * You may obtain a copy of the License at
7223622a50db319d634616311ff74267cf49679e7Alan Viverette *
8223622a50db319d634616311ff74267cf49679e7Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9223622a50db319d634616311ff74267cf49679e7Alan Viverette *
10223622a50db319d634616311ff74267cf49679e7Alan Viverette * Unless required by applicable law or agreed to in writing, software
11223622a50db319d634616311ff74267cf49679e7Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12223622a50db319d634616311ff74267cf49679e7Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13223622a50db319d634616311ff74267cf49679e7Alan Viverette * See the License for the specific language governing permissions and
14223622a50db319d634616311ff74267cf49679e7Alan Viverette * limitations under the License.
15223622a50db319d634616311ff74267cf49679e7Alan Viverette */
16223622a50db319d634616311ff74267cf49679e7Alan Viverette
17223622a50db319d634616311ff74267cf49679e7Alan Viverettepackage android.graphics.drawable;
18223622a50db319d634616311ff74267cf49679e7Alan Viverette
1953d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viveretteimport android.animation.Animator;
20de399397947c5379c61a1003c017b96accbbf545Alan Viveretteimport android.animation.AnimatorListenerAdapter;
2153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viveretteimport android.animation.ObjectAnimator;
22223622a50db319d634616311ff74267cf49679e7Alan Viveretteimport android.animation.TimeInterpolator;
23223622a50db319d634616311ff74267cf49679e7Alan Viveretteimport android.graphics.Canvas;
24ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viveretteimport android.graphics.CanvasProperty;
25223622a50db319d634616311ff74267cf49679e7Alan Viveretteimport android.graphics.Paint;
26223622a50db319d634616311ff74267cf49679e7Alan Viveretteimport android.graphics.Rect;
274d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viveretteimport android.util.MathUtils;
28ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viveretteimport android.view.HardwareCanvas;
29ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viveretteimport android.view.RenderNodeAnimator;
304d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viveretteimport android.view.animation.LinearInterpolator;
31ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
32ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viveretteimport java.util.ArrayList;
33223622a50db319d634616311ff74267cf49679e7Alan Viverette
34223622a50db319d634616311ff74267cf49679e7Alan Viverette/**
353cb07a462be293634e6a83ea6c82f3647cd17dadAlan Viverette * Draws a Material ripple.
36223622a50db319d634616311ff74267cf49679e7Alan Viverette */
37223622a50db319d634616311ff74267cf49679e7Alan Viveretteclass Ripple {
384d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
394b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    private static final TimeInterpolator DECEL_INTERPOLATOR = new LogInterpolator();
4053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
41ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private static final float GLOBAL_SPEED = 1.0f;
42b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette    private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
434b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    private static final float WAVE_TOUCH_UP_ACCELERATION = 3400.0f * GLOBAL_SPEED;
444b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED;
4553d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
464b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    private static final long RIPPLE_ENTER_DELAY = 80;
47b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette
48ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    // Hardware animators.
496ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private final ArrayList<RenderNodeAnimator> mRunningAnimations =
506ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            new ArrayList<RenderNodeAnimator>();
5153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
524d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private final RippleDrawable mOwner;
5353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
54ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    /** Bounds used for computing max radius. */
55223622a50db319d634616311ff74267cf49679e7Alan Viverette    private final Rect mBounds;
5647bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
5753d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    /** Maximum ripple radius. */
5853d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    private float mOuterRadius;
59223622a50db319d634616311ff74267cf49679e7Alan Viverette
604d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /** Screen density used to adjust pixel-based velocities. */
614d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private float mDensity;
624d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
634d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private float mStartingX;
644d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private float mStartingY;
65a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette    private float mClampedStartingX;
66a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette    private float mClampedStartingY;
674d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
68ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    // Hardware rendering properties.
69ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private CanvasProperty<Paint> mPropPaint;
70ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private CanvasProperty<Float> mPropRadius;
71ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private CanvasProperty<Float> mPropX;
72ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private CanvasProperty<Float> mPropY;
73ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
74ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    // Software animators.
75ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private ObjectAnimator mAnimRadius;
76ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private ObjectAnimator mAnimOpacity;
77ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private ObjectAnimator mAnimX;
78ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private ObjectAnimator mAnimY;
79ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    // Temporary paint used for creating canvas properties.
816ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private Paint mTempPaint;
826ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
83ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    // Software rendering properties.
84ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private float mOpacity = 1;
85ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private float mOuterX;
86ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private float mOuterY;
87223622a50db319d634616311ff74267cf49679e7Alan Viverette
884d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    // Values used to tween between the start and end positions.
89dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    private float mTweenRadius = 0;
90dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    private float mTweenX = 0;
91dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    private float mTweenY = 0;
92c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette
93ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    /** Whether we should be drawing hardware animations. */
94ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private boolean mHardwareAnimating;
95223622a50db319d634616311ff74267cf49679e7Alan Viverette
96ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    /** Whether we can use hardware acceleration for the exit animation. */
97ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private boolean mCanUseHardware;
98223622a50db319d634616311ff74267cf49679e7Alan Viverette
99dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    /** Whether we have an explicit maximum radius. */
100dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    private boolean mHasMaxRadius;
101dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
102fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    /** Whether we were canceled externally and should avoid self-removal. */
103fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    private boolean mCanceled;
104fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
1056dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private boolean mHasPendingHardwareExit;
1066dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private int mPendingRadiusDuration;
1076dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private int mPendingOpacityDuration;
1086dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
109223622a50db319d634616311ff74267cf49679e7Alan Viverette    /**
11053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     * Creates a new ripple.
111223622a50db319d634616311ff74267cf49679e7Alan Viverette     */
1124d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public Ripple(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
11353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        mOwner = owner;
114223622a50db319d634616311ff74267cf49679e7Alan Viverette        mBounds = bounds;
115a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette
1164d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mStartingX = startingX;
1174d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mStartingY = startingY;
1184d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
1194d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
120cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette    public void setup(int maxRadius, float density) {
1214d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        if (maxRadius != RippleDrawable.RADIUS_AUTO) {
122dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            mHasMaxRadius = true;
1234d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            mOuterRadius = maxRadius;
1244d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        } else {
1254d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            final float halfWidth = mBounds.width() / 2.0f;
1264d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            final float halfHeight = mBounds.height() / 2.0f;
1274d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
1284d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        }
1294d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
130ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mOuterX = 0;
131ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mOuterY = 0;
1324d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mDensity = density;
133a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette
134a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        clampStartingPosition();
135a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette    }
136a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette
1372ae56403542582bd39c0e522bf29844d59300f37Alan Viverette    public boolean isHardwareAnimating() {
1382ae56403542582bd39c0e522bf29844d59300f37Alan Viverette        return mHardwareAnimating;
1392ae56403542582bd39c0e522bf29844d59300f37Alan Viverette    }
1402ae56403542582bd39c0e522bf29844d59300f37Alan Viverette
141a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette    private void clampStartingPosition() {
142304624100b363137913d1d910690fc83872e41c3Alan Viverette        final float cX = mBounds.exactCenterX();
143304624100b363137913d1d910690fc83872e41c3Alan Viverette        final float cY = mBounds.exactCenterY();
144304624100b363137913d1d910690fc83872e41c3Alan Viverette        final float dX = mStartingX - cX;
145304624100b363137913d1d910690fc83872e41c3Alan Viverette        final float dY = mStartingY - cY;
146a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final float r = mOuterRadius;
147a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        if (dX * dX + dY * dY > r * r) {
148a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            // Point is outside the circle, clamp to the circumference.
149a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            final double angle = Math.atan2(dY, dX);
150304624100b363137913d1d910690fc83872e41c3Alan Viverette            mClampedStartingX = cX + (float) (Math.cos(angle) * r);
151304624100b363137913d1d910690fc83872e41c3Alan Viverette            mClampedStartingY = cY + (float) (Math.sin(angle) * r);
152a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        } else {
153a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            mClampedStartingX = mStartingX;
154a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            mClampedStartingY = mStartingY;
155a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        }
156ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
157ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
158dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public void onHotspotBoundsChanged() {
159dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        if (!mHasMaxRadius) {
160dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            final float halfWidth = mBounds.width() / 2.0f;
161dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            final float halfHeight = mBounds.height() / 2.0f;
162dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
163a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette
164a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            clampStartingPosition();
165dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        }
16653d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
16753d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
168ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    public void setOpacity(float a) {
169ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mOpacity = a;
17053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        invalidateSelf();
17153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
172223622a50db319d634616311ff74267cf49679e7Alan Viverette
173ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    public float getOpacity() {
174ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        return mOpacity;
175ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
176ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
1776ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
178dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public void setRadiusGravity(float r) {
179dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mTweenRadius = r;
1804d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        invalidateSelf();
1814d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
1824d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1836ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
184dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public float getRadiusGravity() {
185dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        return mTweenRadius;
1864d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
1874d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
1886ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
189dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public void setXGravity(float x) {
190dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mTweenX = x;
19153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        invalidateSelf();
19253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
19353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
1946ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
195dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public float getXGravity() {
196dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        return mTweenX;
19747bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    }
19853d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
1996ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
200dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public void setYGravity(float y) {
201dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mTweenY = y;
20253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        invalidateSelf();
20353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
20453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
2056ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    @SuppressWarnings("unused")
206dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette    public float getYGravity() {
207dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        return mTweenY;
20847bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    }
209223622a50db319d634616311ff74267cf49679e7Alan Viverette
210223622a50db319d634616311ff74267cf49679e7Alan Viverette    /**
211ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     * Draws the ripple centered at (0,0) using the specified paint.
21253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     */
213ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    public boolean draw(Canvas c, Paint p) {
214ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final boolean canUseHardware = c.isHardwareAccelerated();
215ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mCanUseHardware != canUseHardware && mCanUseHardware) {
216ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            // We've switched from hardware to non-hardware mode. Panic.
217a0c0ca738989fd3ecad8a54a91f0c6a8b30ad1abAlan Viverette            cancelHardwareAnimations(true);
218ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
219ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mCanUseHardware = canUseHardware;
22053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
221ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final boolean hasContent;
2226dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (canUseHardware && (mHardwareAnimating || mHasPendingHardwareExit)) {
2236dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            hasContent = drawHardware((HardwareCanvas) c, p);
224ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        } else {
225ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            hasContent = drawSoftware(c, p);
226ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
227c3f35b01b5a21e110ca4eedf09c8c6164ab85dfbAlan Viverette
228ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        return hasContent;
229ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
23053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
2316dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private boolean drawHardware(HardwareCanvas c, Paint p) {
2326dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        if (mHasPendingHardwareExit) {
233d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            cancelHardwareAnimations(false);
2346dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            startPendingHardwareExit(c, p);
235223622a50db319d634616311ff74267cf49679e7Alan Viverette        }
236ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
237ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
238ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
239ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        return true;
240223622a50db319d634616311ff74267cf49679e7Alan Viverette    }
241223622a50db319d634616311ff74267cf49679e7Alan Viverette
242ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private boolean drawSoftware(Canvas c, Paint p) {
2434d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        boolean hasContent = false;
24453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
245cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        final int paintAlpha = p.getAlpha();
246cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette        final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
247dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
248dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        if (alpha > 0 && radius > 0) {
249a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            final float x = MathUtils.lerp(
250a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette                    mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
251a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette            final float y = MathUtils.lerp(
252a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette                    mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
25353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette            p.setAlpha(alpha);
254dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            c.drawCircle(x, y, radius, p);
255cc3c573334a9cd2124a8a0ccf2f37884e36f83faAlan Viverette            p.setAlpha(paintAlpha);
25653d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette            hasContent = true;
257223622a50db319d634616311ff74267cf49679e7Alan Viverette        }
25812b61f20a4cdab84b28f153e48c4dcbe82ab6f0aAlan Viverette
25953d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        return hasContent;
260223622a50db319d634616311ff74267cf49679e7Alan Viverette    }
261d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette
26253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    /**
263dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette     * Returns the maximum bounds of the ripple relative to the ripple center.
26453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     */
265d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    public void getBounds(Rect bounds) {
266ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int outerX = (int) mOuterX;
267ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int outerY = (int) mOuterY;
268036a66596dba32c051a016ca9b5334fbbd39c220Alan Viverette        final int r = (int) mOuterRadius + 1;
269ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
270d5154ec2bc7e7c0bdfd14fc784912d390afe43ccAlan Viverette    }
27147bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
27247bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    /**
2734d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * Specifies the starting position relative to the drawable bounds. No-op if
2744d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * the ripple has already entered.
2754d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     */
2764d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public void move(float x, float y) {
2774d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mStartingX = x;
2784d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        mStartingY = y;
279a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette
280a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        clampStartingPosition();
2814d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
2824d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
2834d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    /**
2844d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette     * Starts the enter animation.
28553d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     */
2864d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    public void enter() {
287fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        cancel();
288fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette
289ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int radiusDuration = (int)
2904d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette                (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
291ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
292dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final ObjectAnimator radius = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
293ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        radius.setAutoCancel(true);
294ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        radius.setDuration(radiusDuration);
2954d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        radius.setInterpolator(LINEAR_INTERPOLATOR);
296b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        radius.setStartDelay(RIPPLE_ENTER_DELAY);
297ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
2984d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
299ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cX.setAutoCancel(true);
300ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cX.setDuration(radiusDuration);
301b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        cX.setInterpolator(LINEAR_INTERPOLATOR);
302b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        cX.setStartDelay(RIPPLE_ENTER_DELAY);
303ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
3044d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "yGravity", 1);
305ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cY.setAutoCancel(true);
306ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cY.setDuration(radiusDuration);
307b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        cY.setInterpolator(LINEAR_INTERPOLATOR);
308b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        cY.setStartDelay(RIPPLE_ENTER_DELAY);
309ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
310ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mAnimRadius = radius;
311ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mAnimX = cX;
312ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mAnimY = cY;
313ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
314ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        // Enter animations always run on the UI thread, since it's unlikely
315ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        // that anything interesting is happening until the user lifts their
316ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        // finger.
317ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        radius.start();
318ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cX.start();
319ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cY.start();
32053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
32153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
32253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    /**
323ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     * Starts the exit animation.
32453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     */
32553d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    public void exit() {
326dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
327ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final float remaining;
328ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mAnimRadius != null && mAnimRadius.isRunning()) {
329dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette            remaining = mOuterRadius - radius;
330ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        } else {
331ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            remaining = mOuterRadius;
33253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        }
33353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
3346dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        cancel();
3356dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
336ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int radiusDuration = (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
3374d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette                + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
338ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int opacityDuration = (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
33953d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
340ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mCanUseHardware) {
3416dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette            createPendingHardwareExit(radiusDuration, opacityDuration);
342ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        } else {
3436ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            exitSoftware(radiusDuration, opacityDuration);
344ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
345de399397947c5379c61a1003c017b96accbbf545Alan Viverette    }
346de399397947c5379c61a1003c017b96accbbf545Alan Viverette
3476dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void createPendingHardwareExit(int radiusDuration, int opacityDuration) {
3486dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mHasPendingHardwareExit = true;
3496dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mPendingRadiusDuration = radiusDuration;
3506dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mPendingOpacityDuration = opacityDuration;
3516dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
3526dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        // The animation will start on the next draw().
3536dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        invalidateSelf();
3546dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    }
3556dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
3566dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private void startPendingHardwareExit(HardwareCanvas c, Paint p) {
3576dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mHasPendingHardwareExit = false;
3586dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette
3596dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int radiusDuration = mPendingRadiusDuration;
3606dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final int opacityDuration = mPendingOpacityDuration;
361ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
362a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final float startX = MathUtils.lerp(
363a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette                mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
364a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette        final float startY = MathUtils.lerp(
365a4eab42fe437bff3f8ee9dde264579067ea5cdbdAlan Viverette                mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
366ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
367dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final float startRadius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
3686dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        final Paint paint = getTempPaint(p);
3696dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        paint.setAlpha((int) (paint.getAlpha() * mOpacity + 0.5f));
370ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mPropPaint = CanvasProperty.createPaint(paint);
371dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mPropRadius = CanvasProperty.createFloat(startRadius);
372dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mPropX = CanvasProperty.createFloat(startX);
373dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mPropY = CanvasProperty.createFloat(startY);
374ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
375dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
376dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        radiusAnim.setDuration(radiusDuration);
377b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
3786dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        radiusAnim.setTarget(c);
3796dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        radiusAnim.start();
380ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
381dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
382dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        xAnim.setDuration(radiusDuration);
383b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        xAnim.setInterpolator(DECEL_INTERPOLATOR);
3846dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        xAnim.setTarget(c);
3856dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        xAnim.start();
386ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
387dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
388dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        yAnim.setDuration(radiusDuration);
389b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        yAnim.setInterpolator(DECEL_INTERPOLATOR);
3906dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        yAnim.setTarget(c);
3916dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        yAnim.start();
392ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
393dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
394ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette                RenderNodeAnimator.PAINT_ALPHA, 0);
395dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.setDuration(opacityDuration);
396dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
3976ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        opacityAnim.addListener(mAnimationListener);
3986dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        opacityAnim.setTarget(c);
3996dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        opacityAnim.start();
400de399397947c5379c61a1003c017b96accbbf545Alan Viverette
4016dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mRunningAnimations.add(radiusAnim);
4026dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mRunningAnimations.add(opacityAnim);
4036dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mRunningAnimations.add(xAnim);
4046dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mRunningAnimations.add(yAnim);
405de399397947c5379c61a1003c017b96accbbf545Alan Viverette
406ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mHardwareAnimating = true;
40753d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
408a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        // Set up the software values to match the hardware end values.
409a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        mOpacity = 0;
410a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        mTweenX = 1;
411a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        mTweenY = 1;
412a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        mTweenRadius = 1;
41353d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
41453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
415fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette    /**
416fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Jump all animations to their end state. The caller is responsible for
417fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * removing the ripple from the list of animating ripples.
418fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     */
419d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    public void jump() {
420fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mCanceled = true;
421d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        endSoftwareAnimations();
422a3f0c2b21a73a82a919abe247c4046d114f3712cAlan Viverette        cancelHardwareAnimations(true);
423fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mCanceled = false;
424d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    }
425d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
426d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    private void endSoftwareAnimations() {
427d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mAnimRadius != null) {
428d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mAnimRadius.end();
429a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimRadius = null;
430d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
431d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
432d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mAnimOpacity != null) {
433d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mAnimOpacity.end();
434a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimOpacity = null;
435d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
436d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
437d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mAnimX != null) {
438d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mAnimX.end();
439a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimX = null;
440d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
441d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
442d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        if (mAnimY != null) {
443d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette            mAnimY.end();
444a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimY = null;
445d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette        }
446d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette    }
447d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
4486dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette    private Paint getTempPaint(Paint original) {
4496ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        if (mTempPaint == null) {
4506ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette            mTempPaint = new Paint();
4516ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        }
4526dfa60f33ca6018959ebff1efde82db7d2aed1e3Alan Viverette        mTempPaint.set(original);
4536ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        return mTempPaint;
4546ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    }
4556ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette
4566ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette    private void exitSoftware(int radiusDuration, int opacityDuration) {
457dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
458dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        radiusAnim.setAutoCancel(true);
459dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        radiusAnim.setDuration(radiusDuration);
460b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
461dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
462dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1);
463dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        xAnim.setAutoCancel(true);
464dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        xAnim.setDuration(radiusDuration);
465b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        xAnim.setInterpolator(DECEL_INTERPOLATOR);
466dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
467dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1);
468dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        yAnim.setAutoCancel(true);
469dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        yAnim.setDuration(radiusDuration);
470b996d809bf10a9ad1f6f6be790d0261fe1565f3bAlan Viverette        yAnim.setInterpolator(DECEL_INTERPOLATOR);
471dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
472dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        final ObjectAnimator opacityAnim = ObjectAnimator.ofFloat(this, "opacity", 0);
473dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.setAutoCancel(true);
474dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.setDuration(opacityDuration);
475dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.setInterpolator(LINEAR_INTERPOLATOR);
4766ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        opacityAnim.addListener(mAnimationListener);
47753d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
478dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mAnimRadius = radiusAnim;
479dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        mAnimOpacity = opacityAnim;
4806ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        mAnimX = xAnim;
4816ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        mAnimY = yAnim;
482dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette
483dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        radiusAnim.start();
484dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        opacityAnim.start();
485dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        xAnim.start();
486dc6046fca37264b48e9c959f38d08cfb78f436edAlan Viverette        yAnim.start();
48753d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
48853d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
48953d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    /**
490fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * Cancels all animations. The caller is responsible for removing
491fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette     * the ripple from the list of animating ripples.
49253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette     */
493ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    public void cancel() {
494fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mCanceled = true;
495ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        cancelSoftwareAnimations();
4969bc11ac168d63900589158074028e6c480579421Alan Viverette        cancelHardwareAnimations(false);
497fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mCanceled = false;
498ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
499de399397947c5379c61a1003c017b96accbbf545Alan Viverette
500ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private void cancelSoftwareAnimations() {
501ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mAnimRadius != null) {
502ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            mAnimRadius.cancel();
503a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimRadius = null;
504ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
505de399397947c5379c61a1003c017b96accbbf545Alan Viverette
506ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mAnimOpacity != null) {
507ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            mAnimOpacity.cancel();
508a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimOpacity = null;
509ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
510ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
511ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mAnimX != null) {
512ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            mAnimX.cancel();
513a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimX = null;
514ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
515ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
516ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        if (mAnimY != null) {
517ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            mAnimY.cancel();
518a2362c9251af9d27506aa12f49da42e019665555Alan Viverette            mAnimY = null;
519ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        }
52053d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    }
52153d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette
52253d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    /**
523ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette     * Cancels any running hardware animations.
52447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette     */
5259bc11ac168d63900589158074028e6c480579421Alan Viverette    private void cancelHardwareAnimations(boolean jumpToEnd) {
526ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final ArrayList<RenderNodeAnimator> runningAnimations = mRunningAnimations;
5276ce6d70f9c78f0197f1369246bf55a5f6b8d7ba4Alan Viverette        final int N = runningAnimations.size();
528ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        for (int i = 0; i < N; i++) {
5299bc11ac168d63900589158074028e6c480579421Alan Viverette            if (jumpToEnd) {
5309bc11ac168d63900589158074028e6c480579421Alan Viverette                runningAnimations.get(i).end();
5319bc11ac168d63900589158074028e6c480579421Alan Viverette            } else {
5329bc11ac168d63900589158074028e6c480579421Alan Viverette                runningAnimations.get(i).cancel();
5339bc11ac168d63900589158074028e6c480579421Alan Viverette            }
53447bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        }
535ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        runningAnimations.clear();
536d78a44576c6bac5541e04c1f38599d43c9943653Alan Viverette
5379bc11ac168d63900589158074028e6c480579421Alan Viverette        if (mHasPendingHardwareExit) {
5389bc11ac168d63900589158074028e6c480579421Alan Viverette            // If we had a pending hardware exit, jump to the end state.
5399bc11ac168d63900589158074028e6c480579421Alan Viverette            mHasPendingHardwareExit = false;
5409bc11ac168d63900589158074028e6c480579421Alan Viverette
5419bc11ac168d63900589158074028e6c480579421Alan Viverette            if (jumpToEnd) {
5429bc11ac168d63900589158074028e6c480579421Alan Viverette                mOpacity = 0;
5439bc11ac168d63900589158074028e6c480579421Alan Viverette                mTweenX = 1;
5449bc11ac168d63900589158074028e6c480579421Alan Viverette                mTweenY = 1;
5459bc11ac168d63900589158074028e6c480579421Alan Viverette                mTweenRadius = 1;
5469bc11ac168d63900589158074028e6c480579421Alan Viverette            }
5479bc11ac168d63900589158074028e6c480579421Alan Viverette        }
5489bc11ac168d63900589158074028e6c480579421Alan Viverette
549fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        mHardwareAnimating = false;
550ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
551ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
5524d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    private void removeSelf() {
5534d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette        // The owner will invalidate itself.
554fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        if (!mCanceled) {
555fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette            mOwner.removeRipple(this);
556fdbb98e56d4668c7bfa8de59c3c438c0cb69a535Alan Viverette        }
5574d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette    }
5584d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette
559ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private void invalidateSelf() {
560ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        mOwner.invalidateSelf();
56147bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette    }
56247bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette
563ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
56453d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        @Override
56553d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette        public void onAnimationEnd(Animator animation) {
5664d2f2483f6d9e2eb25d843d676981f4ebc9c79e5Alan Viverette            removeSelf();
56747bf0d95ef6c9ac68773567d503749c874a07f2fAlan Viverette        }
56853d1cfe2d073bff7c7771d5f7dd9108062ddc706Alan Viverette    };
5694b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson
5704b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    /**
5714b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    * Interpolator with a smooth log deceleration
5724b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    */
5734b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    private static final class LogInterpolator implements TimeInterpolator {
5744b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson        @Override
5754b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson        public float getInterpolation(float input) {
5764b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson            return 1 - (float) Math.pow(400, -input * 1.4);
5774b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson        }
5784b65f0ebee11dd95cd4a03329bfa135ec5582badChristian Robertson    }
579223622a50db319d634616311ff74267cf49679e7Alan Viverette}
580