summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java')
-rw-r--r--java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java293
1 files changed, 293 insertions, 0 deletions
diff --git a/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java b/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java
new file mode 100644
index 000000000..3acb2a205
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/utils/FlingAnimationUtils.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui.answer.impl.utils;
+
+import android.animation.Animator;
+import android.content.Context;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/** Utility class to calculate general fling animation when the finger is released. */
+public class FlingAnimationUtils {
+
+ private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
+ private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
+ private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f;
+ private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f;
+ private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
+ private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000;
+
+ /** Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve */
+ private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1.0f / LINEAR_OUT_SLOW_IN_X2;
+
+ private Interpolator linearOutSlowIn;
+
+ private float minVelocityPxPerSecond;
+ private float maxLengthSeconds;
+ private float highVelocityPxPerSecond;
+
+ private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
+ public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
+ this.maxLengthSeconds = maxLengthSeconds;
+ linearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
+ minVelocityPxPerSecond =
+ MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+ highVelocityPxPerSecond =
+ HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ */
+ public void apply(Animator animator, float currValue, float endValue, float velocity) {
+ apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ */
+ public void apply(
+ ViewPropertyAnimator animator, float currValue, float endValue, float velocity) {
+ apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void apply(
+ Animator animator, float currValue, float endValue, float velocity, float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void apply(
+ ViewPropertyAnimator animator,
+ float currValue,
+ float endValue,
+ float velocity,
+ float maxDistance) {
+ AnimatorProperties properties = getProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getProperties(
+ float currValue, float endValue, float velocity, float maxDistance) {
+ float maxLengthSeconds =
+ (float) (this.maxLengthSeconds * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
+ float diff = Math.abs(endValue - currValue);
+ float velAbs = Math.abs(velocity);
+ float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
+ if (durationSeconds <= maxLengthSeconds) {
+ mAnimatorProperties.interpolator = linearOutSlowIn;
+ } else if (velAbs >= minVelocityPxPerSecond) {
+
+ // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
+ durationSeconds = maxLengthSeconds;
+ VelocityInterpolator velocityInterpolator =
+ new VelocityInterpolator(durationSeconds, velAbs, diff);
+ mAnimatorProperties.interpolator =
+ new InterpolatorInterpolator(velocityInterpolator, linearOutSlowIn, linearOutSlowIn);
+ } else {
+
+ // Just use a normal interpolator which doesn't take the velocity into account.
+ durationSeconds = maxLengthSeconds;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ }
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion for the case when the animation is making something
+ * disappear.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void applyDismissing(
+ Animator animator, float currValue, float endValue, float velocity, float maxDistance) {
+ AnimatorProperties properties =
+ getDismissingProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion for the case when the animation is making something
+ * disappear.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ * @param maxDistance the maximum distance for this interaction; the maximum animation length gets
+ * multiplied by the ratio between the actual distance and this value
+ */
+ public void applyDismissing(
+ ViewPropertyAnimator animator,
+ float currValue,
+ float endValue,
+ float velocity,
+ float maxDistance) {
+ AnimatorProperties properties =
+ getDismissingProperties(currValue, endValue, velocity, maxDistance);
+ animator.setDuration(properties.duration);
+ animator.setInterpolator(properties.interpolator);
+ }
+
+ private AnimatorProperties getDismissingProperties(
+ float currValue, float endValue, float velocity, float maxDistance) {
+ float maxLengthSeconds =
+ (float)
+ (this.maxLengthSeconds * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
+ float diff = Math.abs(endValue - currValue);
+ float velAbs = Math.abs(velocity);
+ float y2 = calculateLinearOutFasterInY2(velAbs);
+
+ float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
+ Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
+ float durationSeconds = startGradient * diff / velAbs;
+ if (durationSeconds <= maxLengthSeconds) {
+ mAnimatorProperties.interpolator = mLinearOutFasterIn;
+ } else if (velAbs >= minVelocityPxPerSecond) {
+
+ // Cross fade between linear-out-faster-in and linear interpolator with current
+ // velocity.
+ durationSeconds = maxLengthSeconds;
+ VelocityInterpolator velocityInterpolator =
+ new VelocityInterpolator(durationSeconds, velAbs, diff);
+ InterpolatorInterpolator superInterpolator =
+ new InterpolatorInterpolator(velocityInterpolator, mLinearOutFasterIn, linearOutSlowIn);
+ mAnimatorProperties.interpolator = superInterpolator;
+ } else {
+
+ // Just use a normal interpolator which doesn't take the velocity into account.
+ durationSeconds = maxLengthSeconds;
+ mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
+ }
+ mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+ return mAnimatorProperties;
+ }
+
+ /**
+ * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
+ * velocity. The faster the velocity, the more "linear" the interpolator gets.
+ *
+ * @param velocity the velocity of the gesture.
+ * @return the y2 control point for a cubic bezier path interpolator
+ */
+ private float calculateLinearOutFasterInY2(float velocity) {
+ float t =
+ (velocity - minVelocityPxPerSecond) / (highVelocityPxPerSecond - minVelocityPxPerSecond);
+ t = Math.max(0, Math.min(1, t));
+ return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
+ }
+
+ /** @return the minimum velocity a gesture needs to have to be considered a fling */
+ public float getMinVelocityPxPerSecond() {
+ return minVelocityPxPerSecond;
+ }
+
+ /** An interpolator which interpolates two interpolators with an interpolator. */
+ private static final class InterpolatorInterpolator implements Interpolator {
+
+ private Interpolator mInterpolator1;
+ private Interpolator mInterpolator2;
+ private Interpolator mCrossfader;
+
+ InterpolatorInterpolator(
+ Interpolator interpolator1, Interpolator interpolator2, Interpolator crossfader) {
+ mInterpolator1 = interpolator1;
+ mInterpolator2 = interpolator2;
+ mCrossfader = crossfader;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float t = mCrossfader.getInterpolation(input);
+ return (1 - t) * mInterpolator1.getInterpolation(input)
+ + t * mInterpolator2.getInterpolation(input);
+ }
+ }
+
+ /** An interpolator which interpolates with a fixed velocity. */
+ private static final class VelocityInterpolator implements Interpolator {
+
+ private float mDurationSeconds;
+ private float mVelocity;
+ private float mDiff;
+
+ private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
+ mDurationSeconds = durationSeconds;
+ mVelocity = velocity;
+ mDiff = diff;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float time = input * mDurationSeconds;
+ return time * mVelocity / mDiff;
+ }
+ }
+
+ private static class AnimatorProperties {
+
+ Interpolator interpolator;
+ long duration;
+ }
+}