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;
432411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek    private final float mY2;
441d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
4587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private float mMinVelocityPxPerSecond;
461d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    private float mMaxLengthSeconds;
47efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    private float mHighVelocityPxPerSecond;
48593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mLinearOutSlowInX2;
4987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
504c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
51593ee2084919a44936decd12e624625c18401347Selim Cinek    private PathInterpolator mInterpolator;
52593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mCachedStartGradient = -1;
53593ee2084919a44936decd12e624625c18401347Selim Cinek    private float mCachedVelocityFactor = -1;
544c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
551d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
56593ee2084919a44936decd12e624625c18401347Selim Cinek        this(ctx, maxLengthSeconds, 0.0f);
57593ee2084919a44936decd12e624625c18401347Selim Cinek    }
58593ee2084919a44936decd12e624625c18401347Selim Cinek
59593ee2084919a44936decd12e624625c18401347Selim Cinek    /**
60593ee2084919a44936decd12e624625c18401347Selim Cinek     * @param maxLengthSeconds the longest duration an animation can become in seconds
61593ee2084919a44936decd12e624625c18401347Selim Cinek     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
62593ee2084919a44936decd12e624625c18401347Selim Cinek     *                      the end of the animation. 0 means it's at the beginning and no
63593ee2084919a44936decd12e624625c18401347Selim Cinek     *                      acceleration will take place.
64593ee2084919a44936decd12e624625c18401347Selim Cinek     */
65593ee2084919a44936decd12e624625c18401347Selim Cinek    public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor) {
662411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek        this(ctx, maxLengthSeconds, speedUpFactor, -1.0f, 1.0f);
672411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek    }
682411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek
692411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek    /**
702411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     * @param maxLengthSeconds the longest duration an animation can become in seconds
712411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
722411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     *                      the end of the animation. 0 means it's at the beginning and no
732411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     *                      acceleration will take place.
742411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     * @param x2 the x value to take for the second point of the bezier spline. If a value below 0
752411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     *           is provided, the value is automatically calculated.
762411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     * @param y2 the y value to take for the second point of the bezier spline
772411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek     */
782411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek    public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor, float x2,
792411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek            float y2) {
801d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        mMaxLengthSeconds = maxLengthSeconds;
81593ee2084919a44936decd12e624625c18401347Selim Cinek        mSpeedUpFactor = speedUpFactor;
822411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek        if (x2 < 0) {
832411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek            mLinearOutSlowInX2 = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_X2,
842411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek                    LINEAR_OUT_SLOW_IN_X2_MAX,
852411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek                    mSpeedUpFactor);
862411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek        } else {
872411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek            mLinearOutSlowInX2 = x2;
882411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek        }
892411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek        mY2 = y2;
90593ee2084919a44936decd12e624625c18401347Selim Cinek
9187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        mMinVelocityPxPerSecond
9287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
93efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        mHighVelocityPxPerSecond
94efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi                = HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
9587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
9687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
9787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
9887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
9987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * consistent with the finger motion.
10087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     *
10187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param animator the animator to apply
10287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param currValue the current value
10387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param endValue the end value of the animator
10487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * @param velocity the current velocity of the motion
10587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
106b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void apply(Animator animator, float currValue, float endValue, float velocity) {
1071d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
1081d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
1091d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
1101d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
1111d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
1121d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * consistent with the finger motion.
1131d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *
1141d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param animator the animator to apply
1151d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param currValue the current value
1161d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param endValue the end value of the animator
1171d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param velocity the current velocity of the motion
1184c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
1194c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
1204c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity) {
1214c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
1224c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1234c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1244c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    /**
1254c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
1264c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion.
1274c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
1284c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
1294c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
1304c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
1314c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
1321d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param maxDistance the maximum distance for this interaction; the maximum animation length
1331d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *                    gets multiplied by the ratio between the actual distance and this value
1341d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
135b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void apply(Animator animator, float currValue, float endValue, float velocity,
1361d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            float maxDistance) {
1374c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
1384c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
1394c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
1404c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
1414c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1424c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1434c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    /**
1444c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
1454c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion.
1464c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
1474c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
1484c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
1494c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
1504c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
1514c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param maxDistance the maximum distance for this interaction; the maximum animation length
1524c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *                    gets multiplied by the ratio between the actual distance and this value
1534c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
1544c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
1554c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
1564c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
1574c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
1584c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
1594c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
1604c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
1614c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
1624c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties getProperties(float currValue,
1634c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float endValue, float velocity, float maxDistance) {
1641d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float maxLengthSeconds = (float) (mMaxLengthSeconds
1651d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
16687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        float diff = Math.abs(endValue - currValue);
16787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        float velAbs = Math.abs(velocity);
168593ee2084919a44936decd12e624625c18401347Selim Cinek        float velocityFactor = mSpeedUpFactor == 0.0f
169593ee2084919a44936decd12e624625c18401347Selim Cinek                ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f);
170593ee2084919a44936decd12e624625c18401347Selim Cinek        float startGradient = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
1712411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek                mY2 / mLinearOutSlowInX2, velocityFactor);
172593ee2084919a44936decd12e624625c18401347Selim Cinek        float durationSeconds = startGradient * diff / velAbs;
173593ee2084919a44936decd12e624625c18401347Selim Cinek        Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor);
1741d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        if (durationSeconds <= maxLengthSeconds) {
175593ee2084919a44936decd12e624625c18401347Selim Cinek            mAnimatorProperties.interpolator = slowInInterpolator;
17687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        } else if (velAbs >= mMinVelocityPxPerSecond) {
17787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
17887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
1791d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
18087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            VelocityInterpolator velocityInterpolator
18187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
18287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
183593ee2084919a44936decd12e624625c18401347Selim Cinek                    velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN);
1844c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = superInterpolator;
18587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        } else {
18687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
18787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            // Just use a normal interpolator which doesn't take the velocity into account.
1881d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
189c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
19087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
1914c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
1924c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        return mAnimatorProperties;
19387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
19487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
195593ee2084919a44936decd12e624625c18401347Selim Cinek    private Interpolator getInterpolator(float startGradient, float velocityFactor) {
196593ee2084919a44936decd12e624625c18401347Selim Cinek        if (startGradient != mCachedStartGradient
197593ee2084919a44936decd12e624625c18401347Selim Cinek                || velocityFactor != mCachedVelocityFactor) {
198593ee2084919a44936decd12e624625c18401347Selim Cinek            float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
199593ee2084919a44936decd12e624625c18401347Selim Cinek            mInterpolator = new PathInterpolator(speedup,
200593ee2084919a44936decd12e624625c18401347Selim Cinek                    speedup * startGradient,
2012411f764e196b16f4aedd40733e9e4e9fda7162aSelim Cinek                    mLinearOutSlowInX2, mY2);
202593ee2084919a44936decd12e624625c18401347Selim Cinek            mCachedStartGradient = startGradient;
203593ee2084919a44936decd12e624625c18401347Selim Cinek            mCachedVelocityFactor = velocityFactor;
204593ee2084919a44936decd12e624625c18401347Selim Cinek        }
205593ee2084919a44936decd12e624625c18401347Selim Cinek        return mInterpolator;
206593ee2084919a44936decd12e624625c18401347Selim Cinek    }
207593ee2084919a44936decd12e624625c18401347Selim Cinek
20887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
2091d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * Applies the interpolator and length to the animator, such that the fling animation is
2101d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * consistent with the finger motion for the case when the animation is making something
2111d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * disappear.
2121d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *
2131d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param animator the animator to apply
2141d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param currValue the current value
2151d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param endValue the end value of the animator
2161d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param velocity the current velocity of the motion
2171d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @param maxDistance the maximum distance for this interaction; the maximum animation length
2181d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     *                    gets multiplied by the ratio between the actual distance and this value
2191d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
220b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public void applyDismissing(Animator animator, float currValue, float endValue,
2211d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            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    /**
2294c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * Applies the interpolator and length to the animator, such that the fling animation is
2304c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * consistent with the finger motion for the case when the animation is making something
2314c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * disappear.
2324c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *
2334c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param animator the animator to apply
2344c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param currValue the current value
2354c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param endValue the end value of the animator
2364c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param velocity the current velocity of the motion
2374c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     * @param maxDistance the maximum distance for this interaction; the maximum animation length
2384c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     *                    gets multiplied by the ratio between the actual distance and this value
2394c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek     */
2404c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
2414c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
2424c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
2434c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek                maxDistance);
2444c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setDuration(properties.duration);
2454c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        animator.setInterpolator(properties.interpolator);
2464c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
2474c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
2484c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
2494c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            float velocity, float maxDistance) {
2501d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float maxLengthSeconds = (float) (mMaxLengthSeconds
2511d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
2521d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float diff = Math.abs(endValue - currValue);
2531d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        float velAbs = Math.abs(velocity);
254efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float y2 = calculateLinearOutFasterInY2(velAbs);
255efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi
2562580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi        float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
2572580a976ec93a01ed00fae51364ad872bc591d95Jorim Jaggi        Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
258efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float durationSeconds = startGradient * diff / velAbs;
2591d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        if (durationSeconds <= maxLengthSeconds) {
2604c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = mLinearOutFasterIn;
2611d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        } else if (velAbs >= mMinVelocityPxPerSecond) {
2621d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2631d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // Cross fade between linear-out-faster-in and linear interpolator with current
2641d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // velocity.
2651d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
2661d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            VelocityInterpolator velocityInterpolator
2671d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
2681d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
269593ee2084919a44936decd12e624625c18401347Selim Cinek                    velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN);
2704c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek            mAnimatorProperties.interpolator = superInterpolator;
2711d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        } else {
2721d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2731d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            // Just use a normal interpolator which doesn't take the velocity into account.
2741d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi            durationSeconds = maxLengthSeconds;
275c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
2761d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        }
2774c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
2784c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        return mAnimatorProperties;
2791d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
2801d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
2811d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
282efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
283efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * velocity. The faster the velocity, the more "linear" the interpolator gets.
284efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     *
285efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * @param velocity the velocity of the gesture.
286efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     * @return the y2 control point for a cubic bezier path interpolator
287efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi     */
288efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    private float calculateLinearOutFasterInY2(float velocity) {
289efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        float t = (velocity - mMinVelocityPxPerSecond)
290efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi                / (mHighVelocityPxPerSecond - mMinVelocityPxPerSecond);
291efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        t = Math.max(0, Math.min(1, t));
292efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi        return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
293efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    }
294efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi
295efbd7e3bb8244efae2561f0d9d7cedb36b1730bdJorim Jaggi    /**
2961d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     * @return the minimum velocity a gesture needs to have to be considered a fling
2971d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi     */
2981d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    public float getMinVelocityPxPerSecond() {
2991d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi        return mMinVelocityPxPerSecond;
3001d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    }
3011d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi
3021d480695df31f1c328473f32d5007cea6a03b6e0Jorim Jaggi    /**
30387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * An interpolator which interpolates two interpolators with an interpolator.
30487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
30587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private static final class InterpolatorInterpolator implements Interpolator {
30687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
30787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mInterpolator1;
30887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mInterpolator2;
30987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private Interpolator mCrossfader;
31087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
31187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
31287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                Interpolator crossfader) {
31387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mInterpolator1 = interpolator1;
31487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mInterpolator2 = interpolator2;
31587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mCrossfader = crossfader;
31687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
31787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
31887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        @Override
31987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        public float getInterpolation(float input) {
32087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            float t = mCrossfader.getInterpolation(input);
32187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            return (1 - t) * mInterpolator1.getInterpolation(input)
32287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi                    + t * mInterpolator2.getInterpolation(input);
32387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
32487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
32587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
32687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    /**
32787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     * An interpolator which interpolates with a fixed velocity.
32887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi     */
32987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    private static final class VelocityInterpolator implements Interpolator {
33087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
33187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mDurationSeconds;
33287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mVelocity;
33387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private float mDiff;
33487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
33587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
33687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mDurationSeconds = durationSeconds;
33787cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mVelocity = velocity;
33887cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            mDiff = diff;
33987cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
34087cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi
34187cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        @Override
34287cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        public float getInterpolation(float input) {
34387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            float time = input * mDurationSeconds;
34487cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi            return time * mVelocity / mDiff;
34587cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi        }
34687cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi    }
3474c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
3484c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    private static class AnimatorProperties {
3494c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        Interpolator interpolator;
3504c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek        long duration;
3514c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek    }
3524c6969a512cd70831249ec1d07691f16fe5465f5Selim Cinek
35387cd5e71ec087e191b8baaa8913a878f92d4e10dJorim Jaggi}
354