Merge branch 'master' into honeycomb-release
diff --git a/res/drawable-hdpi/hotseat_browser_normal.png b/res/drawable-hdpi/hotseat_browser_normal.png
index ebdab62..c7f902a 100644
--- a/res/drawable-hdpi/hotseat_browser_normal.png
+++ b/res/drawable-hdpi/hotseat_browser_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_normal.png b/res/drawable-hdpi/hotseat_phone_normal.png
index 2a16f9c..391802e 100644
--- a/res/drawable-hdpi/hotseat_phone_normal.png
+++ b/res/drawable-hdpi/hotseat_phone_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/hotseat_browser_normal.png b/res/drawable-mdpi/hotseat_browser_normal.png
index 4c662fd..4a4a6e3 100644
--- a/res/drawable-mdpi/hotseat_browser_normal.png
+++ b/res/drawable-mdpi/hotseat_browser_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/hotseat_phone_normal.png b/res/drawable-mdpi/hotseat_phone_normal.png
index d759b1f..7f20428 100644
--- a/res/drawable-mdpi/hotseat_phone_normal.png
+++ b/res/drawable-mdpi/hotseat_phone_normal.png
Binary files differ
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 164d7c5..369f0d2 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -91,13 +91,13 @@
     private Rect[] mDragRects = new Rect[8];
     private int[] mDragRectAlphas = new int[mDragRects.length];
     private InterruptibleInOutAnimator[] mDragRectAnims =
-        new InterruptibleInOutAnimator[mDragRects.length];
+            new InterruptibleInOutAnimator[mDragRects.length];
 
     // Used as an index into the above 3 arrays; indicates which is the most current value.
     private int mDragRectCurrent = 0;
 
     private Drawable mCrosshairsDrawable = null;
-    private ValueAnimator mCrosshairsAnimator = null;
+    private InterruptibleInOutAnimator mCrosshairsAnimator = null;
     private float mCrosshairsVisibility = 0.0f;
 
     // When a drag operation is in progress, holds the nearest cell to the touch point
@@ -165,7 +165,7 @@
 
         // Set up the animation for fading the crosshairs in and out
         int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime);
-        mCrosshairsAnimator = new ValueAnimator<Float>(animDuration);
+        mCrosshairsAnimator = new InterruptibleInOutAnimator(animDuration, 0.0f, 1.0f);
         mCrosshairsAnimator.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
                 mCrosshairsVisibility = ((Float) animation.getAnimatedValue()).floatValue();
@@ -207,15 +207,6 @@
         mHover = value;
     }
 
-    private void animateCrosshairsTo(float value) {
-        final ValueAnimator anim = mCrosshairsAnimator;
-        long fullDuration = getResources().getInteger(R.integer.config_crosshairsFadeInTime);
-        anim.setDuration(fullDuration - anim.getCurrentPlayTime());
-        anim.setValues(mCrosshairsVisibility, value);
-        anim.cancel();
-        anim.start();
-    }
-
     public void drawChildren(Canvas canvas) {
         super.dispatchDraw(canvas);
     }
@@ -809,6 +800,11 @@
                 mDragRectAnims[mDragRectCurrent].animateIn();
             }
         }
+
+        // If we are drawing crosshairs, the entire CellLayout needs to be invalidated
+        if (mCrosshairsDrawable != null) {
+            invalidate();
+        }
     }
 
     /**
@@ -1020,7 +1016,7 @@
 
         // Fade out the drag indicators
         if (mCrosshairsAnimator != null) {
-            animateCrosshairsTo(0.0f);
+            mCrosshairsAnimator.animateOut();
         }
 
         mDragRectAnims[mDragRectCurrent].animateOut();
@@ -1070,7 +1066,7 @@
     void onDragEnter(View dragView) {
         // Fade in the drag indicators
         if (mCrosshairsAnimator != null) {
-            animateCrosshairsTo(1.0f);
+            mCrosshairsAnimator.animateIn();
         }
     }
 
@@ -1110,9 +1106,12 @@
      * @param result An array of length 2 in which to store the result (may be null).
      */
     public int[] rectToCell(int width, int height, int[] result) {
+        return rectToCell(getResources(), width, height, result);
+    }
+
+    public static int[] rectToCell(Resources resources, int width, int height, int[] result) {
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
-        final Resources resources = getResources();
         int actualWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
         int actualHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
         int smallerSize = Math.min(actualWidth, actualHeight);
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index b7e5dcd..f3de74b 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -624,8 +624,10 @@
             l.setOnLongClickListener(this);
 
             final Drawable icon = getWidgetIcon(info);
-            final int hSpan = mWorkspaceWidgetLayout.estimateCellHSpan(info.minWidth);
-            final int vSpan = mWorkspaceWidgetLayout.estimateCellHSpan(info.minHeight);
+
+            int[] spans = CellLayout.rectToCell(getResources(), info.minWidth, info.minHeight, null);
+            final int hSpan = spans[0];
+            final int vSpan = spans[1];
 
             ImageView image = (ImageView) l.findViewById(R.id.widget_preview);
             image.setMaxWidth(mMaxWidgetWidth);
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 68dfb35..3c956f0 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -16,8 +16,11 @@
 
 package com.android.launcher2;
 
+import com.android.launcher.R;
+
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -28,13 +31,12 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 
 import java.util.ArrayList;
 
-import com.android.launcher.R;
-
 /**
  * Class for initiating a drag within a view or across multiple views.
  */
@@ -124,6 +126,9 @@
 
     private InputMethodManager mInputMethodManager;
 
+    private int mLastTouch[] = new int[2];
+    private int mDistanceSinceScroll = 0;
+
     /**
      * Interface to receive notifications when a drag starts or stops
      */
@@ -486,15 +491,24 @@
         if (mDeleteRegion != null) {
             inDeleteRegion = mDeleteRegion.contains(x, y);
         }
+
+        // After a scroll, the touch point will still be in the scroll region.
+        // Rather than scrolling immediately, require a bit of twiddling to scroll again
+        final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
+        mDistanceSinceScroll +=
+            Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2));
+        mLastTouch[0] = x;
+        mLastTouch[1] = y;
+
         if (!inDeleteRegion && x < mScrollZone) {
-            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+            if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 mScrollRunnable.setDirection(SCROLL_LEFT);
                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
                 mDragScroller.onEnterScrollArea(SCROLL_LEFT);
             }
         } else if (!inDeleteRegion && x > mScrollView.getWidth() - mScrollZone) {
-            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+            if (mScrollState == SCROLL_OUTSIDE_ZONE && mDistanceSinceScroll > slop) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 mScrollRunnable.setDirection(SCROLL_RIGHT);
                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
@@ -696,6 +710,8 @@
                     mDragScroller.scrollRight();
                 }
                 mScrollState = SCROLL_OUTSIDE_ZONE;
+                mDistanceSinceScroll = 0;
+                mDragScroller.onExitScrollArea();
             }
         }
 
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 2800605..d46ea07 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -414,8 +414,10 @@
             }
 
         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-            // When everything comes back, just reload everything.  We might not
-            // have the right icons for apps on external storage.
+            // First, schedule to add these apps back in.
+            String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
+            // Then, rebind everything.
             startLoader(mApp, false);
 
         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 6802966..97c54c4 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -110,10 +110,14 @@
     private IconCache mIconCache;
     private DragController mDragController;
 
+    // These are temporary variables to prevent having to allocate a new object just to
+    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private int[] mTempCell = new int[2];
     private int[] mTempEstimate = new int[2];
     private float[] mTempOriginXY = new float[2];
     private float[] mTempDragCoordinates = new float[2];
+    private float[] mTempTouchCoordinates = new float[2];
+    private float[] mTempCellLayoutCenterCoordinates = new float[2];
     private float[] mTempDragBottomRightCoordinates = new float[2];
     private Matrix mTempInverseMatrix = new Matrix();
 
@@ -855,7 +859,7 @@
         int originX = x - xOffset;
         int originY = y - yOffset;
         if (mIsSmall || mIsInUnshrinkAnimation) {
-            cellLayout = findMatchingPageForDragOver(dragView, originX, originY);
+            cellLayout = findMatchingPageForDragOver(dragView, originX, originY, xOffset, yOffset);
             if (cellLayout == null) {
                 // cancel the drag if we're not over a mini-screen at time of drop
                 // TODO: maybe add a nice fade here?
@@ -864,7 +868,7 @@
             // get originX and originY in the local coordinate system of the screen
             mTempOriginXY[0] = originX;
             mTempOriginXY[1] = originY;
-            mapPointGlobalToLocal(cellLayout, mTempOriginXY);
+            mapPointFromSelfToChild(cellLayout, mTempOriginXY);
             originX = (int)mTempOriginXY[0];
             originY = (int)mTempOriginXY[1];
         } else {
@@ -954,73 +958,146 @@
         return null;
     }
 
-    private void mapPointGlobalToLocal(View v, float[] xy) {
-        xy[0] = xy[0] + mScrollX - v.getLeft();
-        xy[1] = xy[1] + mScrollY - v.getTop();
-        v.getMatrix().invert(mTempInverseMatrix);
-        mTempInverseMatrix.mapPoints(xy);
+    /*
+    *
+    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+    * coordinate space. The argument xy is modified with the return result.
+    *
+    */
+   void mapPointFromSelfToChild(View v, float[] xy) {
+       mapPointFromSelfToChild(v, xy, null);
+   }
+
+   /*
+    *
+    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+    * coordinate space. The argument xy is modified with the return result.
+    *
+    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
+    * computing it itself; we use this to avoid redudant matrix inversions in
+    * findMatchingPageForDragOver
+    *
+    */
+   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
+       if (cachedInverseMatrix == null) {
+           v.getMatrix().invert(mTempInverseMatrix);
+           cachedInverseMatrix = mTempInverseMatrix;
+       }
+       xy[0] = xy[0] + mScrollX - v.getLeft();
+       xy[1] = xy[1] + mScrollY - v.getTop();
+       cachedInverseMatrix.mapPoints(xy);
+   }
+
+   /*
+    *
+    * Convert the 2D coordinate xy from this CellLayout's coordinate space to
+    * the parent View's coordinate space. The argument xy is modified with the return result.
+    *
+    */
+   void mapPointFromChildToSelf(View v, float[] xy) {
+       v.getMatrix().mapPoints(xy);
+       xy[0] -= (mScrollX - v.getLeft());
+       xy[1] -= (mScrollY - v.getTop());
+   }
+
+    static private float squaredDistance(float[] point1, float[] point2) {
+        float distanceX = point1[0] - point2[0];
+        float distanceY = point2[1] - point2[1];
+        return distanceX * distanceX + distanceY * distanceY;
     }
 
-    // xy = upper left corner of item being dragged
-    // bottomRightXy = lower right corner of item being dragged
-    // This method will see which mini-screen is most overlapped by the item being dragged, and
-    // return it. It will also transform the parameters xy and bottomRightXy into the local
-    // coordinate space of the returned screen
-    private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) {
-        float x = originX + dragView.getScaledDragRegionXOffset();
-        float y = originY + dragView.getScaledDragRegionYOffset();
-        float right = x + dragView.getScaledDragRegionWidth();
-        float bottom = y + dragView.getScaledDragRegionHeight();
+    /*
+     *
+     * Returns true if the passed CellLayout cl overlaps with dragView
+     *
+     */
+    boolean overlaps(CellLayout cl, DragView dragView,
+            int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
+        // Transform the coordinates of the item being dragged to the CellLayout's coordinates
+        final float[] draggedItemTopLeft = mTempDragCoordinates;
+        draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset();
+        draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset();
+        final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
+        draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth();
+        draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight();
 
-        // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
-        // with the item being dragged.
+        // Transform the dragged item's top left coordinates
+        // to the CellLayout's local coordinates
+        mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
+        float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
+        float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
+
+        if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
+            // Transform the dragged item's bottom right coordinates
+            // to the CellLayout's local coordinates
+            mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
+            float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
+            float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
+
+            if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
+                float overlap = (overlapRegionRight - overlapRegionLeft) *
+                         (overlapRegionBottom - overlapRegionTop);
+                if (overlap > 0) {
+                    return true;
+                }
+             }
+        }
+        return false;
+    }
+
+    /*
+     *
+     * This method returns the CellLayout that is currently being dragged to. In order to drag
+     * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
+     * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
+     *
+     * Return null if no CellLayout is currently being dragged over
+     *
+     */
+    private CellLayout findMatchingPageForDragOver(
+            DragView dragView, int originX, int originY, int offsetX, int offsetY) {
+        // We loop through all the screens (ie CellLayouts) and see which ones overlap
+        // with the item being dragged and then choose the one that's closest to the touch point
         final int screenCount = getChildCount();
         CellLayout bestMatchingScreen = null;
         float smallestDistSoFar = Float.MAX_VALUE;
-        final float[] xy = mTempDragCoordinates;
-        final float[] bottomRightXy = mTempDragBottomRightCoordinates;
+
         for (int i = 0; i < screenCount; i++) {
             CellLayout cl = (CellLayout)getChildAt(i);
-            // Transform the coordinates of the item being dragged to the CellLayout's coordinates
-            float left = cl.getLeft();
-            float top = cl.getTop();
-            xy[0] = x + mScrollX - left;
-            xy[1] = y + mScrollY - top;
 
-            bottomRightXy[0] = right + mScrollX - left;
-            bottomRightXy[1] = bottom + mScrollY - top;
+            final float[] touchXy = mTempTouchCoordinates;
+            touchXy[0] = originX + offsetX;
+            touchXy[1] = originY + offsetY;
 
+            // Transform the touch coordinates to the CellLayout's local coordinates
+            // If the touch point is within the bounds of the cell layout, we can return immediately
             cl.getMatrix().invert(mTempInverseMatrix);
-            mTempInverseMatrix.mapPoints(xy);
-            mTempInverseMatrix.mapPoints(bottomRightXy);
+            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
 
-            float dragRegionX = xy[0];
-            float dragRegionY = xy[1];
-            float dragRegionRight = bottomRightXy[0];
-            float dragRegionBottom = bottomRightXy[1];
-            float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f;
-            float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f;
+            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
+                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
+                return cl;
+            }
 
-            // Find the overlapping region
-            float overlapLeft = Math.max(0f, dragRegionX);
-            float overlapTop = Math.max(0f, dragRegionY);
-            float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
-            float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
-            if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
-                    (overlapTop >= 0 && overlapBottom <= cl.getHeight())) {
-                // Calculate the distance between the two centers
-                float distX = dragRegionCenterX - cl.getWidth()/2;
-                float distY = dragRegionCenterY - cl.getHeight()/2;
-                float dist = distX * distX + distY * distY;
+            if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) {
+                // Get the center of the cell layout in screen coordinates
+                final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
+                cellLayoutCenter[0] = cl.getWidth()/2;
+                cellLayoutCenter[1] = cl.getHeight()/2;
+                mapPointFromChildToSelf(cl, cellLayoutCenter);
 
-                float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
+                touchXy[0] = originX + offsetX;
+                touchXy[1] = originY + offsetY;
 
-                // Calculate the closest overlapping region
-                if (overlap > 0 && dist < smallestDistSoFar) {
+                // Calculate the distance between the center of the CellLayout
+                // and the touch point
+                float dist = squaredDistance(touchXy, cellLayoutCenter);
+
+                if (dist < smallestDistSoFar) {
                     smallestDistSoFar = dist;
                     bestMatchingScreen = cl;
                 }
-             }
+            }
         }
 
         if (bestMatchingScreen != mDragTargetLayout) {
@@ -1039,7 +1116,8 @@
         int originX = x - xOffset;
         int originY = y - yOffset;
         if (mIsSmall || mIsInUnshrinkAnimation) {
-            currentLayout = findMatchingPageForDragOver(dragView, originX, originY);
+            currentLayout = findMatchingPageForDragOver(
+                    dragView, originX, originY, xOffset, yOffset);
 
             if (currentLayout == null) {
                 return;
@@ -1049,7 +1127,7 @@
             // get originX and originY in the local coordinate system of the screen
             mTempOriginXY[0] = originX;
             mTempOriginXY[1] = originY;
-            mapPointGlobalToLocal(currentLayout, mTempOriginXY);
+            mapPointFromSelfToChild(currentLayout, mTempOriginXY);
             originX = (int)mTempOriginXY[0];
             originY = (int)mTempOriginXY[1];
         } else {
@@ -1069,7 +1147,7 @@
             }
         }
 
-        if (source != this) {
+        if (source instanceof AllAppsPagedView) {
             // This is a hack to fix the point used to determine which cell an icon from the all
             // apps screen is over
             if (item != null && item.spanX == 1 && currentLayout != null) {
@@ -1233,7 +1311,8 @@
             int xOffset, int yOffset, DragView dragView, Object dragInfo) {
         CellLayout layout;
         if (mIsSmall || mIsInUnshrinkAnimation) {
-            layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset);
+            layout = findMatchingPageForDragOver(
+                    dragView, x - xOffset, y - yOffset, xOffset, yOffset);
             if (layout == null) {
                 // cancel the drag if we're not over a mini-screen at time of drop
                 return false;
diff --git a/src/com/android/launcher2/allapps.rs b/src/com/android/launcher2/allapps.rs
index acee82b..a9727fb 100644
--- a/src/com/android/launcher2/allapps.rs
+++ b/src/com/android/launcher2/allapps.rs
@@ -37,7 +37,6 @@
 VpConsts_t *vpConstants;
 
 
-#pragma rs export_var(COLUMNS_PER_PAGE_PORTRAIT, ROWS_PER_PAGE_PORTRAIT, COLUMNS_PER_PAGE_LANDSCAPE, ROWS_PER_PAGE_LANDSCAPE, gIconCount, gSelectedIconIndex, gSelectedIconTexture, gHomeButton, gTargetPos, gPFTexNearest, gPFTexMip, gPFTexMipAlpha, gPVCurve, gPS, gSMCell, gIconIDs, gLabelIDs, vpConstants)
 #pragma rs export_func(move, moveTo, setZoom, fling)
 
 
@@ -47,7 +46,7 @@
 static float g_PhysicsTableSize = 7;
 
 static float gZoomTarget;
-static float gTargetPos;
+float gTargetPos;
 static float g_PosPage = 0.f;
 static float g_PosVelocity = 0.f;
 static float g_LastPositionX = 0.f;