Merge "Updating fling to delete anim" into ub-launcher3-burnaby
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 7cf002e..683c511 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -117,7 +117,7 @@
     }
 
     @Override
-    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { }
+    public void onFlingToDelete(DragObject d, PointF vec) { }
 
     @Override
     public final void onDragEnter(DragObject d) {
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index e741b97..08186f5 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -17,30 +17,18 @@
 package com.android.launcher3;
 
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.os.AsyncTask;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.util.FlingAnimation;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.WidgetsContainerView;
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
-    private static int FLING_DELETE_ANIMATION_DURATION = 350;
-    private static float FLING_TO_DELETE_FRICTION = 0.035f;
-    private static int MODE_FLING_DELETE_TO_TRASH = 0;
-    private static int MODE_FLING_DELETE_ALONG_VECTOR = 1;
-
-    private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
-
     public DeleteDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -119,146 +107,19 @@
         return true;
     }
 
-    /**
-     * Creates an animation from the current drag view to the delete trash icon.
-     */
-    private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
-            DragObject d, PointF vel, ViewConfiguration config) {
-
-        int width = mDrawable.getIntrinsicWidth();
-        int height = mDrawable.getIntrinsicHeight();
-        final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
-                width, height);
-        final Rect from = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-
-        // Calculate how far along the velocity vector we should put the intermediate point on
-        // the bezier curve
-        float velocity = Math.abs(vel.length());
-        float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f));
-        int offsetY = (int) (-from.top * vp);
-        int offsetX = (int) (offsetY / (vel.y / vel.x));
-        final float y2 = from.top + offsetY;                        // intermediate t/l
-        final float x2 = from.left + offsetX;
-        final float x1 = from.left;                                 // drag view t/l
-        final float y1 = from.top;
-        final float x3 = to.left;                                   // delete target t/l
-        final float y3 = to.top;
-
-        final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() {
-            @Override
-            public float getInterpolation(float t) {
-                return t * t * t * t * t * t * t * t;
-            }
-        };
-        return new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final DragView dragView = (DragView) dragLayer.getAnimatedView();
-                float t = ((Float) animation.getAnimatedValue()).floatValue();
-                float tp = scaleAlphaInterpolator.getInterpolation(t);
-                float initialScale = dragView.getInitialScale();
-                float finalAlpha = 0.5f;
-                float scale = dragView.getScaleX();
-                float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f;
-                float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f;
-                float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) +
-                        (t * t) * x3;
-                float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) +
-                        (t * t) * y3;
-
-                dragView.setTranslationX(x);
-                dragView.setTranslationY(y);
-                dragView.setScaleX(initialScale * (1f - tp));
-                dragView.setScaleY(initialScale * (1f - tp));
-                dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp));
-            }
-        };
-    }
-
-    /**
-     * Creates an animation from the current drag view along its current velocity vector.
-     * For this animation, the alpha runs for a fixed duration and we update the position
-     * progressively.
-     */
-    private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
-        private DragLayer mDragLayer;
-        private PointF mVelocity;
-        private Rect mFrom;
-        private long mPrevTime;
-        private boolean mHasOffsetForScale;
-        private float mFriction;
-
-        private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
-
-        public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from,
-                long startTime, float friction) {
-            mDragLayer = dragLayer;
-            mVelocity = vel;
-            mFrom = from;
-            mPrevTime = startTime;
-            mFriction = 1f - (dragLayer.getResources().getDisplayMetrics().density * friction);
-        }
-
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            final DragView dragView = (DragView) mDragLayer.getAnimatedView();
-            float t = ((Float) animation.getAnimatedValue()).floatValue();
-            long curTime = AnimationUtils.currentAnimationTimeMillis();
-
-            if (!mHasOffsetForScale) {
-                mHasOffsetForScale = true;
-                float scale = dragView.getScaleX();
-                float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f;
-                float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f;
-
-                mFrom.left += xOffset;
-                mFrom.top += yOffset;
-            }
-
-            mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
-            mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
-
-            dragView.setTranslationX(mFrom.left);
-            dragView.setTranslationY(mFrom.top);
-            dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
-
-            mVelocity.x *= mFriction;
-            mVelocity.y *= mFriction;
-            mPrevTime = curTime;
-        }
-    };
-    private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer,
-            DragObject d, PointF vel, final long startTime, final int duration,
-            ViewConfiguration config) {
-        final Rect from = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-
-        return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime,
-                FLING_TO_DELETE_FRICTION);
-    }
-
-    public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
-        final boolean isWidgets = d.dragSource instanceof WidgetsContainerView;
-        final boolean isAllapps = d.dragSource instanceof AppsContainerView;
-
+    @Override
+    public void onFlingToDelete(final DragObject d, PointF vel) {
         // Don't highlight the icon as it's animating
         d.dragView.setColor(0);
         d.dragView.updateInitialScaleToCurrentScale();
-        // Don't highlight the target if we are flinging from AllApps
-        if (isWidgets || isAllapps) {
-            resetHoverColor();
-        }
 
-        if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
-            // Defer animating out the drop target if we are animating to it
-            mSearchDropTargetBar.deferOnDragEnd();
-            mSearchDropTargetBar.finishAnimations();
-        }
-
-        final ViewConfiguration config = ViewConfiguration.get(mLauncher);
         final DragLayer dragLayer = mLauncher.getDragLayer();
-        final int duration = FLING_DELETE_ANIMATION_DURATION;
+        FlingAnimation fling = new FlingAnimation(d, vel,
+                getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
+                        mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()),
+                        dragLayer);
+
+        final int duration = fling.getDuration();
         final long startTime = AnimationUtils.currentAnimationTimeMillis();
 
         // NOTE: Because it takes time for the first frame of animation to actually be
@@ -282,27 +143,17 @@
                 return Math.min(1f, mOffset + t);
             }
         };
-        AnimatorUpdateListener updateCb = null;
-        if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
-            updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config);
-        } else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) {
-            updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
-                    duration, config);
-        }
 
         Runnable onAnimationEndRunnable = new Runnable() {
             @Override
             public void run() {
-                // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up
-                // itself, otherwise, complete the drop to initiate the deletion process
-                if (!isWidgets || !isAllapps) {
-                    mLauncher.exitSpringLoadedDragMode();
-                    completeDrop(d);
-                }
+                mLauncher.exitSpringLoadedDragMode();
+                completeDrop(d);
                 mLauncher.getDragController().onDeferredEndFling(d);
             }
         };
-        dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable,
+
+        dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable,
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index a896099..5fea9d8 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -710,8 +710,7 @@
         mDragObject.dragComplete = true;
         mFlingToDeleteDropTarget.onDragExit(mDragObject);
         if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
-            mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y,
-                    vel);
+            mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, vel);
             accepted = true;
         }
         mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true,
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 3628e57..a3828c1 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -29,7 +29,7 @@
 
     public static final String TAG = "DropTarget";
 
-    class DragObject {
+    public static class DragObject {
         public int x = -1;
         public int y = -1;
 
@@ -164,7 +164,7 @@
      * of onDrop().  (This is only called on objects that are set as the DragController's
      * fling-to-delete target.
      */
-    void onFlingToDelete(DragObject dragObject, int x, int y, PointF vec);
+    void onFlingToDelete(DragObject dragObject, PointF vec);
 
     /**
      * Check if a drop action can occur at, or near, the requested location.
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index ed8eea7..a282805 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -841,7 +841,8 @@
         return true;
     }
 
-    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
+    @Override
+    public void onFlingToDelete(DragObject d, PointF vec) {
         // Do nothing
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 07d1c98..4c76092 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3917,7 +3917,7 @@
     }
 
     @Override
-    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
+    public void onFlingToDelete(DragObject d, PointF vec) {
         // Do nothing
     }
 
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
new file mode 100644
index 0000000..55c5d7d
--- /dev/null
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -0,0 +1,104 @@
+package com.android.launcher3.util;
+
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.animation.DecelerateInterpolator;
+
+import com.android.launcher3.DragLayer;
+import com.android.launcher3.DragView;
+import com.android.launcher3.DropTarget.DragObject;
+
+public class FlingAnimation implements AnimatorUpdateListener {
+
+    /**
+     * Maximum acceleration in one dimension (pixels per milliseconds)
+     */
+    private static final float MAX_ACCELERATION = 0.5f;
+    private static final int DRAG_END_DELAY = 300;
+
+    protected final DragObject mDragObject;
+    protected final Rect mIconRect;
+    protected final DragLayer mDragLayer;
+    protected final Rect mFrom;
+    protected final int mDuration;
+    protected final float mUX, mUY;
+    protected final float mAnimationTimeFraction;
+    protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+
+    protected float mAX, mAY;
+
+    /**
+     * @param vel initial fling velocity in pixels per second.
+     */
+    public FlingAnimation(DragObject d, PointF vel, Rect iconRect, DragLayer dragLayer) {
+        mDragObject = d;
+        mUX = vel.x / 1000;
+        mUY = vel.y / 1000;
+        mIconRect = iconRect;
+
+        mDragLayer = dragLayer;
+        mFrom = new Rect();
+        dragLayer.getViewRectRelativeToSelf(d.dragView, mFrom);
+
+        float scale = d.dragView.getScaleX();
+        float xOffset = ((scale - 1f) * d.dragView.getMeasuredWidth()) / 2f;
+        float yOffset = ((scale - 1f) * d.dragView.getMeasuredHeight()) / 2f;
+        mFrom.left += xOffset;
+        mFrom.right -= xOffset;
+        mFrom.top += yOffset;
+        mFrom.bottom -= yOffset;
+
+        mDuration = initDuration();
+        mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
+    }
+
+    /**
+     * The fling animation is based on the following system
+     *   - Apply a constant force in the y direction to causing the fling to decelerate.
+     *   - The animation runs for the time taken by the object to go out of the screen.
+     *   - Calculate a constant acceleration in x direction such that the object reaches
+     *     {@link #mIconRect} in the given time.
+     */
+    protected int initDuration() {
+        float sY = -mFrom.bottom;
+
+        float d = mUY * mUY + 2 * sY * MAX_ACCELERATION;
+        if (d >= 0) {
+            // sY can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for y direction.
+            mAY = MAX_ACCELERATION;
+        } else {
+            // sY is not reachable, decrease the acceleration so that sY is almost reached.
+            d = 0;
+            mAY = mUY * mUY / (2 * -sY);
+        }
+        double t = (-mUY - Math.sqrt(d)) / mAY;
+
+        float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX();
+
+        // Find horizontal acceleration such that: u*t + a*t*t/2 = s
+        mAX = (float) ((sX - t * mUX) * 2 / (t * t));
+        return (int) Math.round(t);
+    }
+
+    public final int getDuration() {
+        return mDuration + DRAG_END_DELAY;
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        float t = animation.getAnimatedFraction();
+        if (t > mAnimationTimeFraction) {
+            t = 1;
+        } else {
+            t = t / mAnimationTimeFraction;
+        }
+        final DragView dragView = (DragView) mDragLayer.getAnimatedView();
+        final float time = t * mDuration;
+        dragView.setTranslationX(time * mUX + mFrom.left + mAX * time * time / 2);
+        dragView.setTranslationY(time * mUY + mFrom.top + mAY * time * time / 2);
+        dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+    }
+}