1f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette/*
2f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * Copyright (C) 2015 The Android Open Source Project
3f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette *
4f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * you may not use this file except in compliance with the License.
6f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * You may obtain a copy of the License at
7f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette *
8f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette *
10f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * Unless required by applicable law or agreed to in writing, software
11f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * See the License for the specific language governing permissions and
14f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * limitations under the License.
15f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette */
16f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
17f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverettepackage android.graphics.drawable;
18f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
19f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.animation.Animator;
20f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.animation.AnimatorListenerAdapter;
21f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.animation.AnimatorSet;
22f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.animation.ObjectAnimator;
23f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.animation.TimeInterpolator;
24f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.graphics.Canvas;
25f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.graphics.CanvasProperty;
26f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.graphics.Paint;
27f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.graphics.Rect;
28f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.util.FloatProperty;
29f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.util.MathUtils;
30f6829a0a618b4523619ec53c996b04d67e3186b9Chris Craikimport android.view.DisplayListCanvas;
31f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.view.RenderNodeAnimator;
32f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteimport android.view.animation.LinearInterpolator;
33f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
34f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette/**
35f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette * Draws a ripple foreground.
36f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette */
37f872ee0057ed247aa93589347f1b53afc99517f8Alan Viveretteclass RippleForeground extends RippleComponent {
38f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
39f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final TimeInterpolator DECELERATE_INTERPOLATOR = new LogDecelerateInterpolator(
40f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            400f, 1.4f, 0);
41f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
42f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    // Pixel-based accelerations and velocities.
43f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
44f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
45f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
46f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
476a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    // Bounded ripple animation properties.
486a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final int BOUNDED_ORIGIN_EXIT_DURATION = 300;
496a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final int BOUNDED_RADIUS_EXIT_DURATION = 800;
506a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
516a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final float MAX_BOUNDED_RADIUS = 350;
526a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
53f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final int RIPPLE_ENTER_DELAY = 80;
54f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final int OPACITY_ENTER_DURATION_FAST = 120;
55f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
566a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    // Parent-relative values for starting position.
57f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mStartingX;
58f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mStartingY;
59f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mClampedStartingX;
60f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mClampedStartingY;
61f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
62f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    // Hardware rendering properties.
63f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private CanvasProperty<Paint> mPropPaint;
64f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private CanvasProperty<Float> mPropRadius;
65f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private CanvasProperty<Float> mPropX;
66f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private CanvasProperty<Float> mPropY;
67f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
686a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    // Target values for tween animations.
696a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float mTargetX = 0;
706a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float mTargetY = 0;
716a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
726a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    /** Ripple target radius used when bounded. Not used for clamping. */
736a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float mBoundedRadius = 0;
746a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
75f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    // Software rendering properties.
76f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mOpacity = 1;
77f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
78f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    // Values used to tween between the start and end positions.
79f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mTweenRadius = 0;
80f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mTweenX = 0;
81f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private float mTweenY = 0;
82f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
836a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    /** Whether this ripple is bounded. */
846a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private boolean mIsBounded;
856a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
86f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /** Whether this ripple has finished its exit animation. */
87f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private boolean mHasFinishedExit;
88f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
896a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
90b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi            boolean isBounded, boolean forceSoftware) {
91b7303a36baf8d0ac3efdeeee3310ef5974ba9ceaJorim Jaggi        super(owner, bounds, forceSoftware);
92f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
936a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        mIsBounded = isBounded;
94f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mStartingX = startingX;
95f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mStartingY = startingY;
96f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
976a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        if (isBounded) {
986a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            mBoundedRadius = MAX_BOUNDED_RADIUS * 0.9f
996a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                    + (float) (MAX_BOUNDED_RADIUS * Math.random() * 0.1);
1006a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        } else {
1016a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            mBoundedRadius = 0;
1026a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        }
103f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
104f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
105f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
106f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected void onTargetRadiusChanged(float targetRadius) {
107f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        clampStartingPosition();
108f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
109f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
110f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
111f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected boolean drawSoftware(Canvas c, Paint p) {
112f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        boolean hasContent = false;
113f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
114f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final int origAlpha = p.getAlpha();
115f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final int alpha = (int) (origAlpha * mOpacity + 0.5f);
1166a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final float radius = getCurrentRadius();
117f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        if (alpha > 0 && radius > 0) {
1186a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            final float x = getCurrentX();
1196a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            final float y = getCurrentY();
120f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            p.setAlpha(alpha);
121f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            c.drawCircle(x, y, radius, p);
122f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            p.setAlpha(origAlpha);
123f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            hasContent = true;
124f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
125f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
126f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return hasContent;
127f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
128f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
129f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
130f6829a0a618b4523619ec53c996b04d67e3186b9Chris Craik    protected boolean drawHardware(DisplayListCanvas c) {
131f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
132f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return true;
133f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
134f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
135f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
136f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * Returns the maximum bounds of the ripple relative to the ripple center.
137f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
138f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    public void getBounds(Rect bounds) {
1396a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int outerX = (int) mTargetX;
1406a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int outerY = (int) mTargetY;
141f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final int r = (int) mTargetRadius + 1;
142f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
143f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
144f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
145f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
146f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * Specifies the starting position relative to the drawable bounds. No-op if
147f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * the ripple has already entered.
148f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
149f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    public void move(float x, float y) {
150f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mStartingX = x;
151f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mStartingY = y;
152f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
153f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        clampStartingPosition();
154f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
155f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
156f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
157f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * @return {@code true} if this ripple has finished its exit animation
158f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
159f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    public boolean hasFinishedExit() {
160f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return mHasFinishedExit;
161f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
162f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
163f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
164f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected Animator createSoftwareEnter(boolean fast) {
1656a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        // Bounded ripples don't have enter animations.
1666a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        if (mIsBounded) {
1676a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            return null;
1686a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        }
1696a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
170f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final int duration = (int)
171bd5294bcb857a48ad22ddd54b13208ec2903c3f6Alan Viverette                (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
172f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
1736a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
1746a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setAutoCancel(true);
1756a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setDuration(duration);
1766a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
1776a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
1786a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
1796a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
1806a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setAutoCancel(true);
1816a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setDuration(duration);
1826a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
1836a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
184f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
185f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
186f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setAutoCancel(true);
187f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setDuration(OPACITY_ENTER_DURATION_FAST);
188f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setInterpolator(LINEAR_INTERPOLATOR);
189f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
190f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final AnimatorSet set = new AnimatorSet();
1916a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        set.play(tweenOrigin).with(tweenRadius).with(opacity);
192f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
193f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return set;
194f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
195f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
1966a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float getCurrentX() {
1976a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
1986a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    }
1996a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
2006a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float getCurrentY() {
2016a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
2026a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    }
2036a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
204f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private int getRadiusExitDuration() {
2056a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final float remainingRadius = mTargetRadius - getCurrentRadius();
2066a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
207bd5294bcb857a48ad22ddd54b13208ec2903c3f6Alan Viverette                + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
208f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
209f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
2106a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private float getCurrentRadius() {
2116a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
2126a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    }
2136a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
214f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private int getOpacityExitDuration() {
215f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
216f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
217f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
2186a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    /**
2196a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette     * Compute target values that are dependent on bounding.
2206a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette     */
2216a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private void computeBoundedTargetValues() {
2226a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        mTargetX = (mClampedStartingX - mBounds.exactCenterX()) * .7f;
2236a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        mTargetY = (mClampedStartingY - mBounds.exactCenterY()) * .7f;
2246a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        mTargetRadius = mBoundedRadius;
2256a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    }
2266a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
227f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
228f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected Animator createSoftwareExit() {
2296a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int radiusDuration;
2306a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int originDuration;
2316a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int opacityDuration;
2326a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        if (mIsBounded) {
2336a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            computeBoundedTargetValues();
2346a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
2356a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
2366a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
2376a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
2386a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        } else {
2396a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            radiusDuration = getRadiusExitDuration();
2406a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            originDuration = radiusDuration;
2416a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            opacityDuration = getOpacityExitDuration();
2426a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        }
2436a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
2446a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
2456a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setAutoCancel(true);
2466a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setDuration(radiusDuration);
2476a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
248f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
2496a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
2506a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setAutoCancel(true);
2516a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setDuration(originDuration);
2526a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
253f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
254f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
255f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setAutoCancel(true);
256f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setDuration(opacityDuration);
257f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setInterpolator(LINEAR_INTERPOLATOR);
258f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
259f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final AnimatorSet set = new AnimatorSet();
2606a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        set.play(tweenOrigin).with(tweenRadius).with(opacity);
261f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        set.addListener(mAnimationListener);
262f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
263f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return set;
264f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
265f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
266f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
267f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
2686a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int radiusDuration;
2696a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int originDuration;
2706a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final int opacityDuration;
2716a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        if (mIsBounded) {
2726a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            computeBoundedTargetValues();
2736a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
2746a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
2756a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
2766a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
2776a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        } else {
2786a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            radiusDuration = getRadiusExitDuration();
2796a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            originDuration = radiusDuration;
2806a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            opacityDuration = getOpacityExitDuration();
2816a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        }
282f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
2836a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final float startX = getCurrentX();
2846a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final float startY = getCurrentY();
2856a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final float startRadius = getCurrentRadius();
286f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
287f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
288f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
289f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mPropPaint = CanvasProperty.createPaint(p);
290f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mPropRadius = CanvasProperty.createFloat(startRadius);
291f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mPropX = CanvasProperty.createFloat(startX);
292f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mPropY = CanvasProperty.createFloat(startY);
293f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
294f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
295f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        radius.setDuration(radiusDuration);
296f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        radius.setInterpolator(DECELERATE_INTERPOLATOR);
297f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
2986a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
2996a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        x.setDuration(originDuration);
300f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        x.setInterpolator(DECELERATE_INTERPOLATOR);
301f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
3026a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
3036a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette        y.setDuration(originDuration);
304f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        y.setInterpolator(DECELERATE_INTERPOLATOR);
305f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
306f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
307f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette                RenderNodeAnimator.PAINT_ALPHA, 0);
308f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setDuration(opacityDuration);
309f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.setInterpolator(LINEAR_INTERPOLATOR);
310f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        opacity.addListener(mAnimationListener);
311f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
312f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
313f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        set.add(radius);
314f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        set.add(opacity);
315f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        set.add(x);
316f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        set.add(y);
317f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
318f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        return set;
319f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
320f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
321f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    @Override
322f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    protected void jumpValuesToExit() {
323f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mOpacity = 0;
324f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mTweenX = 1;
325f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mTweenY = 1;
326f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        mTweenRadius = 1;
327f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
328f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
329f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
330f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * Clamps the starting position to fit within the ripple bounds.
331f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
332f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private void clampStartingPosition() {
333f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final float cX = mBounds.exactCenterX();
334f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final float cY = mBounds.exactCenterY();
335f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final float dX = mStartingX - cX;
336f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final float dY = mStartingY - cY;
337f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        final float r = mTargetRadius;
338f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        if (dX * dX + dY * dY > r * r) {
339f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            // Point is outside the circle, clamp to the perimeter.
340f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            final double angle = Math.atan2(dY, dX);
341f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mClampedStartingX = cX + (float) (Math.cos(angle) * r);
342f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mClampedStartingY = cY + (float) (Math.sin(angle) * r);
343f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        } else {
344f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mClampedStartingX = mStartingX;
345f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mClampedStartingY = mStartingY;
346f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
347f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
348f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
349f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
350f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
351f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public void onAnimationEnd(Animator animator) {
352f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mHasFinishedExit = true;
353f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
354f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    };
355f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
356f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
357f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    * Interpolator with a smooth log deceleration.
358f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    */
359f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final class LogDecelerateInterpolator implements TimeInterpolator {
360f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        private final float mBase;
361f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        private final float mDrift;
362f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        private final float mTimeScale;
363f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        private final float mOutputScale;
364f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
365f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public LogDecelerateInterpolator(float base, float timeScale, float drift) {
366f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mBase = base;
367f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mDrift = drift;
368f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mTimeScale = 1f / timeScale;
369f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
370f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            mOutputScale = 1f / computeLog(1f);
371f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
372f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
373f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        private float computeLog(float t) {
374f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
375f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
376f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
377f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
378f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public float getInterpolation(float t) {
379f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            return computeLog(t) * mOutputScale;
380f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
381f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    }
382f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
383f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
3846a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette     * Property for animating radius between its initial and target values.
385f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
3866a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final FloatProperty<RippleForeground> TWEEN_RADIUS =
3876a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            new FloatProperty<RippleForeground>("tweenRadius") {
388f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
389f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public void setValue(RippleForeground object, float value) {
390f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            object.mTweenRadius = value;
391f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            object.invalidateSelf();
392f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
393f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
394f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
395f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public Float get(RippleForeground object) {
396f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            return object.mTweenRadius;
397f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
398f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    };
399f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
400f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    /**
4016a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette     * Property for animating origin between its initial and target values.
4026a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette     */
4036a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
4046a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            new FloatProperty<RippleForeground>("tweenOrigin") {
4056a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                @Override
4066a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                public void setValue(RippleForeground object, float value) {
4076a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                    object.mTweenX = value;
4086a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                    object.mTweenY = value;
4096a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                    object.invalidateSelf();
4106a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                }
4116a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
4126a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                @Override
4136a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                public Float get(RippleForeground object) {
4146a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                    return object.mTweenX;
4156a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette                }
4166a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette            };
4176a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette
4186a67db41388165aca63d0d5de2830cc096ed930bAlan Viverette    /**
419f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     * Property for animating opacity between 0 and its target value.
420f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette     */
421f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    private static final FloatProperty<RippleForeground> OPACITY =
422f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            new FloatProperty<RippleForeground>("opacity") {
423f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
424f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public void setValue(RippleForeground object, float value) {
425f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            object.mOpacity = value;
426f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            object.invalidateSelf();
427f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
428f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette
429f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        @Override
430f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        public Float get(RippleForeground object) {
431f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette            return object.mOpacity;
432f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette        }
433f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette    };
434f872ee0057ed247aa93589347f1b53afc99517f8Alan Viverette}
435