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