1 package com.android.launcher3.util; 2 3 import android.animation.TimeInterpolator; 4 import android.animation.ValueAnimator; 5 import android.animation.ValueAnimator.AnimatorUpdateListener; 6 import android.graphics.PointF; 7 import android.graphics.Rect; 8 import android.view.animation.DecelerateInterpolator; 9 10 import com.android.launcher3.DragLayer; 11 import com.android.launcher3.DragView; 12 import com.android.launcher3.DropTarget.DragObject; 13 14 public class FlingAnimation implements AnimatorUpdateListener { 15 16 /** 17 * Maximum acceleration in one dimension (pixels per milliseconds) 18 */ 19 private static final float MAX_ACCELERATION = 0.5f; 20 private static final int DRAG_END_DELAY = 300; 21 22 protected final DragObject mDragObject; 23 protected final Rect mIconRect; 24 protected final DragLayer mDragLayer; 25 protected final Rect mFrom; 26 protected final int mDuration; 27 protected final float mUX, mUY; 28 protected final float mAnimationTimeFraction; 29 protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); 30 31 protected float mAX, mAY; 32 33 /** 34 * @param vel initial fling velocity in pixels per second. 35 */ 36 public FlingAnimation(DragObject d, PointF vel, Rect iconRect, DragLayer dragLayer) { 37 mDragObject = d; 38 mUX = vel.x / 1000; 39 mUY = vel.y / 1000; 40 mIconRect = iconRect; 41 42 mDragLayer = dragLayer; 43 mFrom = new Rect(); 44 dragLayer.getViewRectRelativeToSelf(d.dragView, mFrom); 45 46 float scale = d.dragView.getScaleX(); 47 float xOffset = ((scale - 1f) * d.dragView.getMeasuredWidth()) / 2f; 48 float yOffset = ((scale - 1f) * d.dragView.getMeasuredHeight()) / 2f; 49 mFrom.left += xOffset; 50 mFrom.right -= xOffset; 51 mFrom.top += yOffset; 52 mFrom.bottom -= yOffset; 53 54 mDuration = initDuration(); 55 mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY); 56 } 57 58 /** 59 * The fling animation is based on the following system 60 * - Apply a constant force in the y direction to causing the fling to decelerate. 61 * - The animation runs for the time taken by the object to go out of the screen. 62 * - Calculate a constant acceleration in x direction such that the object reaches 63 * {@link #mIconRect} in the given time. 64 */ 65 protected int initDuration() { 66 float sY = -mFrom.bottom; 67 68 float d = mUY * mUY + 2 * sY * MAX_ACCELERATION; 69 if (d >= 0) { 70 // sY can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for y direction. 71 mAY = MAX_ACCELERATION; 72 } else { 73 // sY is not reachable, decrease the acceleration so that sY is almost reached. 74 d = 0; 75 mAY = mUY * mUY / (2 * -sY); 76 } 77 double t = (-mUY - Math.sqrt(d)) / mAY; 78 79 float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX(); 80 81 // Find horizontal acceleration such that: u*t + a*t*t/2 = s 82 mAX = (float) ((sX - t * mUX) * 2 / (t * t)); 83 return (int) Math.round(t); 84 } 85 86 public final int getDuration() { 87 return mDuration + DRAG_END_DELAY; 88 } 89 90 @Override 91 public void onAnimationUpdate(ValueAnimator animation) { 92 float t = animation.getAnimatedFraction(); 93 if (t > mAnimationTimeFraction) { 94 t = 1; 95 } else { 96 t = t / mAnimationTimeFraction; 97 } 98 final DragView dragView = (DragView) mDragLayer.getAnimatedView(); 99 final float time = t * mDuration; 100 dragView.setTranslationX(time * mUX + mFrom.left + mAX * time * time / 2); 101 dragView.setTranslationY(time * mUY + mFrom.top + mAY * time * time / 2); 102 dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); 103 } 104 } 105