Merge "Dragging app from workspace to App Info doesn't consume the app." into ub-launcher3-master
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9a9b57a..6d8fa6b 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,7 +19,6 @@
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.PointF;
-import android.os.AsyncTask;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AnimationUtils;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 66cf2df..69ef826 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -372,7 +372,7 @@
      * When {@code true}, hotseat is on the bottom row when in landscape mode.
      * If {@code false}, hotseat is on the right column when in landscape mode.
      */
-    boolean isVerticalBarLayout() {
+    public boolean isVerticalBarLayout() {
         return isLandscape && transposeLayoutWithOrientation;
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index e72f341..4a39e6b 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -42,7 +42,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Thunk;
 
 import com.android.launcher3.R;
@@ -657,21 +656,29 @@
 
         ViewConfiguration config = ViewConfiguration.get(mLauncher);
         mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
-
+        PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+        float theta = MAX_FLING_DEGREES + 1;
         if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
             // Do a quick dot product test to ensure that we are flinging upwards
-            PointF vel = new PointF(mVelocityTracker.getXVelocity(),
-                    mVelocityTracker.getYVelocity());
             PointF upVec = new PointF(0f, -1f);
-            float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
-                    (vel.length() * upVec.length()));
-            if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
-                return vel;
-            }
+            theta = getAngleBetweenVectors(vel, upVec);
+        } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
+                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Remove icon is on left side instead of top, so check if we are flinging to the left.
+            PointF leftVec = new PointF(-1f, 0f);
+            theta = getAngleBetweenVectors(vel, leftVec);
+        }
+        if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
+            return vel;
         }
         return null;
     }
 
+    private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
+        return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
+                (vec1.length() * vec2.length()));
+    }
+
     void drop(DropTarget dropTarget, float x, float y, PointF flingVel) {
         final int[] coordinates = mCoordinatesTemp;
 
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index f82038b..da8bae7 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -51,7 +51,7 @@
         mFrom.top += yOffset;
         mFrom.bottom -= yOffset;
 
-        mDuration = initDuration();
+        mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration();
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
     }
 
@@ -62,7 +62,7 @@
      *   - Calculate a constant acceleration in x direction such that the object reaches
      *     {@link #mIconRect} in the given time.
      */
-    protected int initDuration() {
+    protected int initFlingUpDuration() {
         float sY = -mFrom.bottom;
 
         float d = mUY * mUY + 2 * sY * MAX_ACCELERATION;
@@ -83,6 +83,34 @@
         return (int) Math.round(t);
     }
 
+    /**
+     * The fling animation is based on the following system
+     *   - Apply a constant force in the x 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 y direction such that the object reaches
+     *     {@link #mIconRect} in the given time.
+     */
+    protected int initFlingLeftDuration() {
+        float sX = -mFrom.right;
+
+        float d = mUX * mUX + 2 * sX * MAX_ACCELERATION;
+        if (d >= 0) {
+            // sX can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for x direction.
+            mAX = MAX_ACCELERATION;
+        } else {
+            // sX is not reachable, decrease the acceleration so that sX is almost reached.
+            d = 0;
+            mAX = mUX * mUX / (2 * -sX);
+        }
+        double t = (-mUX - Math.sqrt(d)) / mAX;
+
+        float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY();
+
+        // Find vertical acceleration such that: u*t + a*t*t/2 = s
+        mAY = (float) ((sY - t * mUY) * 2 / (t * t));
+        return (int) Math.round(t);
+    }
+
     public final int getDuration() {
         return mDuration + DRAG_END_DELAY;
     }