FlingAnimationUtils.java revision 593ee2084919a44936decd12e624625c18401347
187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi/*
287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * Copyright (C) 2014 The Android Open Source Project
387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi *
487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License");
587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * you may not use this file except in compliance with the License.
687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * You may obtain a copy of the License at
787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi *
887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi *      http://www.apache.org/licenses/LICENSE-2.0
987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi *
1087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * Unless required by applicable law or agreed to in writing, software
1187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS,
1287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * See the License for the specific language governing permissions and
1487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * limitations under the License
1587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi */
1687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
1787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggipackage com.android.systemui.statusbar;
1887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
19b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.animation.Animator;
2087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggiimport android.content.Context;
214c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinekimport android.view.ViewPropertyAnimator;
2287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggiimport android.view.animation.Interpolator;
2387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggiimport android.view.animation.PathInterpolator;
2487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
25c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators;
26593ee2084919a44936decd12e624625c18401347Selim Cinekimport com.android.systemui.statusbar.notification.NotificationUtils;
27c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
2887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi/**
2987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi * Utility class to calculate general fling animation when the finger is released.
3087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi */
3187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggipublic class FlingAnimationUtils {
3287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
331d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
34593ee2084919a44936decd12e624625c18401347Selim Cinek    private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f;
352580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi    private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
362580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi    private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f;
372580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi    private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f;
3887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
39efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000;
4087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
41593ee2084919a44936decd12e624625c18401347Selim Cinek    private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 0.75f;
42593ee2084919a44936decd12e624625c18401347Selim Cinek    private final float mSpeedUpFactor;
431d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
4487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private float mMinVelocityPxPerSecond;
451d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    private float mMaxLengthSeconds;
46efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    private float mHighVelocityPxPerSecond;
47593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mLinearOutSlowInX2;
4887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
494c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
50593ee2084919a44936decd12e624625c18401347Selim Cinek    private PathInterpolator mInterpolator;
51593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mCachedStartGradient = -1;
52593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mCachedVelocityFactor = -1;
534c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
541d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
55593ee2084919a44936decd12e624625c18401347Selim Cinek        this(ctx, maxLengthSeconds, 0.0f);
56593ee2084919a44936decd12e624625c18401347Selim Cinek    }
57593ee2084919a44936decd12e624625c18401347Selim Cinek
58593ee2084919a44936decd12e624625c18401347Selim Cinek    /**
59593ee2084919a44936decd12e624625c18401347Selim Cinek     * @param maxLengthSeconds the longest duration an animation can become in seconds
60593ee2084919a44936decd12e624625c18401347Selim Cinek     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
61593ee2084919a44936decd12e624625c18401347Selim Cinek     *                      the end of the animation. 0 means it's at the beginning and no
62593ee2084919a44936decd12e624625c18401347Selim Cinek     *                      acceleration will take place.
63593ee2084919a44936decd12e624625c18401347Selim Cinek     */
64593ee2084919a44936decd12e624625c18401347Selim Cinek    public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor) {
651d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        mMaxLengthSeconds = maxLengthSeconds;
66593ee2084919a44936decd12e624625c18401347Selim Cinek        mSpeedUpFactor = speedUpFactor;
67593ee2084919a44936decd12e624625c18401347Selim Cinek        mLinearOutSlowInX2 = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_X2,
68593ee2084919a44936decd12e624625c18401347Selim Cinek                LINEAR_OUT_SLOW_IN_X2_MAX,
69593ee2084919a44936decd12e624625c18401347Selim Cinek                mSpeedUpFactor);
70593ee2084919a44936decd12e624625c18401347Selim Cinek
7187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        mMinVelocityPxPerSecond
7287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
73efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        mHighVelocityPxPerSecond
74efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi                = HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
7587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
7687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
7787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
7887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
7987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * consistent with the finger motion.
8087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     *
8187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param animator the animator to apply
8287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param currValue the current value
8387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param endValue the end value of the animator
8487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param velocity the current velocity of the motion
8587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
86b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void apply(Animator animator, float currValue, float endValue, float velocity) {
871d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
881d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
891d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
901d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
911d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
921d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * consistent with the finger motion.
931d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *
941d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param animator the animator to apply
951d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param currValue the current value
961d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param endValue the end value of the animator
971d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param velocity the current velocity of the motion
984c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
994c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
1004c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity) {
1014c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
1024c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1034c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1044c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    /**
1054c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
1064c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion.
1074c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
1084c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
1094c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
1104c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
1114c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
1121d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param maxDistance the maximum distance for this interaction; the maximum animation length
1131d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *                    gets multiplied by the ratio between the actual distance and this value
1141d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
115b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void apply(Animator animator, float currValue, float endValue, float velocity,
1161d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            float maxDistance) {
1174c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
1184c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
1194c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
1204c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
1214c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1224c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1234c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    /**
1244c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
1254c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion.
1264c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
1274c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
1284c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
1294c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
1304c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
1314c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param maxDistance the maximum distance for this interaction; the maximum animation length
1324c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *                    gets multiplied by the ratio between the actual distance and this value
1334c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
1344c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
1354c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
1364c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
1374c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
1384c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
1394c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
1404c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1414c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1424c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties getProperties(float currValue,
1434c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float endValue, float velocity, float maxDistance) {
1441d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float maxLengthSeconds = (float) (mMaxLengthSeconds
1451d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
14687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        float diff = Math.abs(endValue - currValue);
14787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        float velAbs = Math.abs(velocity);
148593ee2084919a44936decd12e624625c18401347Selim Cinek        float velocityFactor = mSpeedUpFactor == 0.0f
149593ee2084919a44936decd12e624625c18401347Selim Cinek                ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f);
150593ee2084919a44936decd12e624625c18401347Selim Cinek        float startGradient = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
151593ee2084919a44936decd12e624625c18401347Selim Cinek                1.0f / mLinearOutSlowInX2, velocityFactor);
152593ee2084919a44936decd12e624625c18401347Selim Cinek        float durationSeconds = startGradient * diff / velAbs;
153593ee2084919a44936decd12e624625c18401347Selim Cinek        Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor);
1541d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        if (durationSeconds <= maxLengthSeconds) {
155593ee2084919a44936decd12e624625c18401347Selim Cinek            mAnimatorProperties.interpolator = slowInInterpolator;
15687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        } else if (velAbs >= mMinVelocityPxPerSecond) {
15787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
15887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
1591d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
16087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            VelocityInterpolator velocityInterpolator
16187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
16287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
163593ee2084919a44936decd12e624625c18401347Selim Cinek                    velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN);
1644c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = superInterpolator;
16587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        } else {
16687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
16787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            // Just use a normal interpolator which doesn't take the velocity into account.
1681d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
169c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
17087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
1714c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
1724c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        return mAnimatorProperties;
17387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
17487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
175593ee2084919a44936decd12e624625c18401347Selim Cinek    private Interpolator getInterpolator(float startGradient, float velocityFactor) {
176593ee2084919a44936decd12e624625c18401347Selim Cinek        if (startGradient != mCachedStartGradient
177593ee2084919a44936decd12e624625c18401347Selim Cinek                || velocityFactor != mCachedVelocityFactor) {
178593ee2084919a44936decd12e624625c18401347Selim Cinek            float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
179593ee2084919a44936decd12e624625c18401347Selim Cinek            mInterpolator = new PathInterpolator(speedup,
180593ee2084919a44936decd12e624625c18401347Selim Cinek                    speedup * startGradient,
181593ee2084919a44936decd12e624625c18401347Selim Cinek                    mLinearOutSlowInX2, 1);
182593ee2084919a44936decd12e624625c18401347Selim Cinek            mCachedStartGradient = startGradient;
183593ee2084919a44936decd12e624625c18401347Selim Cinek            mCachedVelocityFactor = velocityFactor;
184593ee2084919a44936decd12e624625c18401347Selim Cinek        }
185593ee2084919a44936decd12e624625c18401347Selim Cinek        return mInterpolator;
186593ee2084919a44936decd12e624625c18401347Selim Cinek    }
187593ee2084919a44936decd12e624625c18401347Selim Cinek
18887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
1891d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
1901d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * consistent with the finger motion for the case when the animation is making something
1911d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * disappear.
1921d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *
1931d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param animator the animator to apply
1941d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param currValue the current value
1951d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param endValue the end value of the animator
1961d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param velocity the current velocity of the motion
1971d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param maxDistance the maximum distance for this interaction; the maximum animation length
1981d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *                    gets multiplied by the ratio between the actual distance and this value
1991d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
200b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void applyDismissing(Animator animator, float currValue, float endValue,
2011d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            float velocity, float maxDistance) {
2024c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
2034c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
2044c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
2054c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
2064c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
2074c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
2084c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    /**
2094c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
2104c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion for the case when the animation is making something
2114c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * disappear.
2124c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
2134c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
2144c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
2154c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
2164c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
2174c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param maxDistance the maximum distance for this interaction; the maximum animation length
2184c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *                    gets multiplied by the ratio between the actual distance and this value
2194c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
2204c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
2214c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
2224c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
2234c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
2244c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
2254c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
2264c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
2274c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
2284c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
2294c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
2301d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float maxLengthSeconds = (float) (mMaxLengthSeconds
2311d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
2321d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float diff = Math.abs(endValue - currValue);
2331d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float velAbs = Math.abs(velocity);
234efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float y2 = calculateLinearOutFasterInY2(velAbs);
235efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi
2362580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi        float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
2372580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi        Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
238efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float durationSeconds = startGradient * diff / velAbs;
2391d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        if (durationSeconds <= maxLengthSeconds) {
2404c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = mLinearOutFasterIn;
2411d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        } else if (velAbs >= mMinVelocityPxPerSecond) {
2421d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2431d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // Cross fade between linear-out-faster-in and linear interpolator with current
2441d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // velocity.
2451d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
2461d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            VelocityInterpolator velocityInterpolator
2471d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
2481d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
249593ee2084919a44936decd12e624625c18401347Selim Cinek                    velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN);
2504c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = superInterpolator;
2511d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        } else {
2521d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2531d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // Just use a normal interpolator which doesn't take the velocity into account.
2541d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
255c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
2561d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        }
2574c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
2584c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        return mAnimatorProperties;
2591d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
2601d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2611d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
262efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
263efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * velocity. The faster the velocity, the more "linear" the interpolator gets.
264efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     *
265efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * @param velocity the velocity of the gesture.
266efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * @return the y2 control point for a cubic bezier path interpolator
267efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     */
268efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    private float calculateLinearOutFasterInY2(float velocity) {
269efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float t = (velocity - mMinVelocityPxPerSecond)
270efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi                / (mHighVelocityPxPerSecond - mMinVelocityPxPerSecond);
271efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        t = Math.max(0, Math.min(1, t));
272efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
273efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    }
274efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi
275efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    /**
2761d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @return the minimum velocity a gesture needs to have to be considered a fling
2771d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
2781d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    public float getMinVelocityPxPerSecond() {
2791d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        return mMinVelocityPxPerSecond;
2801d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
2811d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2821d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
28387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * An interpolator which interpolates two interpolators with an interpolator.
28487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
28587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private static final class InterpolatorInterpolator implements Interpolator {
28687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
28787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mInterpolator1;
28887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mInterpolator2;
28987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mCrossfader;
29087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
29187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
29287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                Interpolator crossfader) {
29387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mInterpolator1 = interpolator1;
29487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mInterpolator2 = interpolator2;
29587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mCrossfader = crossfader;
29687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
29787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
29887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        @Override
29987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        public float getInterpolation(float input) {
30087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            float t = mCrossfader.getInterpolation(input);
30187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            return (1 - t) * mInterpolator1.getInterpolation(input)
30287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                    + t * mInterpolator2.getInterpolation(input);
30387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
30487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
30587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
30687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
30787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * An interpolator which interpolates with a fixed velocity.
30887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
30987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private static final class VelocityInterpolator implements Interpolator {
31087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
31187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mDurationSeconds;
31287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mVelocity;
31387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mDiff;
31487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
31587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
31687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mDurationSeconds = durationSeconds;
31787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mVelocity = velocity;
31887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mDiff = diff;
31987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
32087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
32187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        @Override
32287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        public float getInterpolation(float input) {
32387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            float time = input * mDurationSeconds;
32487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            return time * mVelocity / mDiff;
32587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
32687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
3274c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
3284c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private static class AnimatorProperties {
3294c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        Interpolator interpolator;
3304c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        long duration;
3314c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
3324c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
33387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi}
334