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