wip 3029324: Implement new drag feedback

- Show page borders when dragging
- Enlarge scroll region (landscape only)
- When inside scroll region, show green border on adjacent page
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 3f9945f..d26b2f9 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -41,8 +41,6 @@
 import android.view.animation.Interpolator;
 import android.view.animation.LayoutAnimationController;
 
-import java.util.Arrays;
-
 public class CellLayout extends ViewGroup implements Dimmable {
     static final String TAG = "CellLayout";
 
@@ -75,12 +73,13 @@
     private OnTouchListener mInterceptTouchListener;
 
     private float mBackgroundAlpha;
-    private final Rect mBackgroundLayoutRect = new Rect();
 
     private Drawable mBackground;
     private Drawable mBackgroundMini;
     private Drawable mBackgroundMiniHover;
-    // If we're actively dragging something over this screen and it's small, mHover is true
+    private Drawable mBackgroundHover;
+
+    // If we're actively dragging something over this screen, mHover is true
     private boolean mHover = false;
 
     private final Point mDragCenter = new Point();
@@ -154,6 +153,8 @@
             mBackground.setFilterBitmap(true);
             mBackgroundMiniHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover);
             mBackgroundMiniHover.setFilterBitmap(true);
+            mBackgroundHover = res.getDrawable(R.drawable.home_screen_bg_hover);
+            mBackgroundHover.setFilterBitmap(true);
         }
 
         // Initialize the data structures used for the drag visualization.
@@ -215,27 +216,25 @@
         anim.start();
     }
 
+    public void drawChildren(Canvas canvas) {
+        super.dispatchDraw(canvas);
+    }
+
     @Override
-    public void dispatchDraw(Canvas canvas) {
+    protected void onDraw(Canvas canvas) {
         if (mBackgroundAlpha > 0.0f) {
             Drawable bg;
-            if (mHover && getScaleX() < 0.5f) {
-                bg = mBackgroundMiniHover;
-            } else if (getScaleX() < 0.5f) {
-                bg = mBackgroundMini;
+            if (getScaleX() < 0.5f) {
+                bg = mHover ? mBackgroundMiniHover : mBackgroundMini;
             } else {
-                bg = mBackground;
+                bg = mHover ? mBackgroundHover : mBackground;
             }
             if (bg != null) {
                 bg.setAlpha((int) (mBackgroundAlpha * 255));
                 bg.draw(canvas);
             }
         }
-        super.dispatchDraw(canvas);
-    }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
         if (mCrosshairsVisibility > 0.0f) {
             final int countX = mCountX;
             final int countY = mCountY;
@@ -641,15 +640,17 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mBackgroundLayoutRect.set(0, 0, w, h);
         if (mBackground != null) {
-            mBackground.setBounds(mBackgroundLayoutRect);
+            mBackground.setBounds(0, 0, w, h);
+        }
+        if (mBackgroundHover != null) {
+            mBackgroundHover.setBounds(0, 0, w, h);
         }
         if (mBackgroundMiniHover != null) {
-            mBackgroundMiniHover.setBounds(mBackgroundLayoutRect);
+            mBackgroundMiniHover.setBounds(0, 0, w, h);
         }
         if (mBackgroundMini != null) {
-            mBackgroundMini.setBounds(mBackgroundLayoutRect);
+            mBackgroundMini.setBounds(0, 0, w, h);
         }
     }
 
@@ -1016,7 +1017,6 @@
         mDragCell[1] = -1;
 
         setHover(false);
-        invalidate();
 
         // Fade out the drag indicators
         if (mCrosshairsAnimator != null) {
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index d544340..68dfb35 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -56,8 +56,8 @@
     private static final int SCROLL_OUTSIDE_ZONE = 0;
     private static final int SCROLL_WAITING_IN_ZONE = 1;
 
-    private static final int SCROLL_LEFT = 0;
-    private static final int SCROLL_RIGHT = 1;
+    static final int SCROLL_LEFT = 0;
+    static final int SCROLL_RIGHT = 1;
 
     private Context mContext;
     private Handler mHandler;
@@ -156,6 +156,10 @@
         mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
     }
 
+    public boolean dragging() {
+        return mDragging;
+    }
+
     /**
      * Starts a drag.
      *
@@ -487,18 +491,21 @@
                 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) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 mScrollRunnable.setDirection(SCROLL_RIGHT);
                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+                mDragScroller.onEnterScrollArea(SCROLL_RIGHT);
             }
         } else {
             if (mScrollState == SCROLL_WAITING_IN_ZONE) {
                 mScrollState = SCROLL_OUTSIDE_ZONE;
                 mScrollRunnable.setDirection(SCROLL_RIGHT);
                 mHandler.removeCallbacks(mScrollRunnable);
+                mDragScroller.onExitScrollArea();
             }
         }
     }
diff --git a/src/com/android/launcher2/DragScroller.java b/src/com/android/launcher2/DragScroller.java
index c3c251c..6af9c30 100644
--- a/src/com/android/launcher2/DragScroller.java
+++ b/src/com/android/launcher2/DragScroller.java
@@ -23,4 +23,16 @@
 public interface DragScroller {
     void scrollLeft();
     void scrollRight();
+
+    /**
+     * The touch point has entered the scroll area; a scroll is imminent.
+     *
+     * @param direction The scroll direction
+     */
+    void onEnterScrollArea(int direction);
+
+    /**
+     * The touch point has left the scroll area.
+     */
+    void onExitScrollArea();
 }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index c065170..f5a5935 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -2029,7 +2029,7 @@
             final Canvas c = new Canvas(bitmap);
             c.scale(scale, scale);
             c.translate(-cell.getLeftPadding(), -cell.getTopPadding());
-            cell.dispatchDraw(c);
+            cell.drawChildren(c);
 
             image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background));
             image.setImageBitmap(bitmap);
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 732bfbd..bf0eb12 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -133,6 +133,8 @@
     // (SmoothPagedView does this)
     protected boolean mDeferScrollUpdate = false;
 
+    protected boolean mIsPageMoving = false;
+
     /**
      * Simple cache mechanism for PagedViewIcon outlines.
      */
@@ -159,11 +161,6 @@
         void onPageSwitch(View newPage, int newPageIndex);
     }
 
-    public interface PageMovingListener {
-        void onPageBeginMoving();
-        void onPageEndMoving();
-    }
-
     public PagedView(Context context) {
         this(context, null);
     }
@@ -257,12 +254,22 @@
         }
     }
 
-    // a method that subclasses can override to add behavior
-    protected void pageBeginMoving() {
+    private void pageBeginMoving() {
+        mIsPageMoving = true;
+        onPageBeginMoving();
+    }
+
+    private void pageEndMoving() {
+        onPageEndMoving();
+        mIsPageMoving = false;
     }
 
     // a method that subclasses can override to add behavior
-    protected void pageEndMoving() {
+    protected void onPageBeginMoving() {
+    }
+
+    // a method that subclasses can override to add behavior
+    protected void onPageEndMoving() {
     }
 
     /**
@@ -657,12 +664,9 @@
 
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
-                // Release the drag
-                pageEndMoving();
                 mTouchState = TOUCH_STATE_REST;
                 mAllowLongPress = false;
                 mActivePointerId = INVALID_POINTER;
-
                 break;
 
             case MotionEvent.ACTION_POINTER_UP:
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 47e172f..d9bff5e 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -33,9 +33,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Camera;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -78,9 +76,6 @@
     private static final int BACKGROUND_FADE_OUT_DURATION = 300;
     private static final int BACKGROUND_FADE_IN_DURATION = 100;
 
-    static final int SCROLL_RIGHT = 0;
-    static final int SCROLL_LEFT = 1;
-
     // These animators are used to fade the
     private ObjectAnimator<Float> mBackgroundFadeIn;
     private ObjectAnimator<Float> mBackgroundFadeOut;
@@ -126,11 +121,14 @@
     private Drawable mPreviousIndicator;
     private Drawable mNextIndicator;
 
-    // State variable that indicated whether the pages are small (ie when you're
+    // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
     private boolean mIsSmall;
+
     private AnimatorListener mUnshrinkAnimationListener;
 
+    private boolean mInScrollArea = false;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -389,7 +387,7 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-    protected void pageBeginMoving() {
+    protected void onPageBeginMoving() {
         if (mNextPage != INVALID_PAGE) {
             // we're snapping to a particular screen
             enableChildrenCache(mCurrentPage, mNextPage);
@@ -401,9 +399,13 @@
         showOutlines();
     }
 
-    protected void pageEndMoving() {
+    protected void onPageEndMoving() {
         clearChildrenCache();
-        hideOutlines();
+
+        // Hide the outlines, as long as we're not dragging
+        if (!mDragController.dragging()) {
+            hideOutlines();
+        }
     }
 
     @Override
@@ -500,11 +502,11 @@
                 getRelativeChildOffset(mCurrentPage) + halfScreenSize);
 
         float scrollProgress = Math.abs(delta/(pageWidth*1.0f + mPageSpacing));
-        int scrollDirection = delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT;
+        boolean scrollRight = (delta <= 0);
 
         float rotation;
 
-        if (scrollDirection == SCROLL_RIGHT) {
+        if (scrollRight) {
             rotation = -scrollProgress * WORKSPACE_ROTATION;
             cur.setRotationY(rotation);
             cur.setScaleX(getScaleXForRotation(rotation));
@@ -933,6 +935,7 @@
     public void onDragEnter(DragSource source, int x, int y, int xOffset,
             int yOffset, DragView dragView, Object dragInfo) {
         getCurrentDropLayout().onDragEnter(dragView);
+        showOutlines();
     }
 
     public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -1050,6 +1053,7 @@
                 mDragTargetLayout.onDragExit();
             }
             mDragTargetLayout = bestMatchingScreen;
+            // TODO: Should we be calling mDragTargetLayout.onDragEnter() here?
         }
         return bestMatchingScreen;
     }
@@ -1103,23 +1107,27 @@
                 }
             }
         }
-        if (currentLayout != mDragTargetLayout) {
-            if (mDragTargetLayout != null) {
-                mDragTargetLayout.onDragExit();
-                currentLayout.onDragEnter(dragView);
-            }
-            mDragTargetLayout = currentLayout;
-        }
 
-        // only visualize the drop locations for moving icons within the home screen on tablet
-        // on phone, we also visualize icons dragged in from All Apps
-        if ((!LauncherApplication.isScreenXLarge() || source == this)
-                && mDragTargetLayout != null) {
-            final View child = (mDragInfo == null) ? null : mDragInfo.cell;
-            int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
-            int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
-            mDragTargetLayout.visualizeDropLocation(
-                    child, localOriginX, localOriginY, item.spanX, item.spanY);
+        // When touch is inside the scroll area, skip dragOver actions for the current screen
+        if (!mInScrollArea) {
+            if (currentLayout != mDragTargetLayout) {
+                if (mDragTargetLayout != null) {
+                    mDragTargetLayout.onDragExit();
+                }
+                currentLayout.onDragEnter(dragView);
+                mDragTargetLayout = currentLayout;
+            }
+
+            // only visualize the drop locations for moving icons within the home screen on tablet
+            // on phone, we also visualize icons dragged in from All Apps
+            if ((!LauncherApplication.isScreenXLarge() || source == this)
+                    && mDragTargetLayout != null) {
+                final View child = (mDragInfo == null) ? null : mDragInfo.cell;
+                int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
+                int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
+                mDragTargetLayout.visualizeDropLocation(
+                        child, localOriginX, localOriginY, item.spanX, item.spanY);
+            }
         }
     }
 
@@ -1129,6 +1137,9 @@
             mDragTargetLayout.onDragExit();
             mDragTargetLayout = null;
         }
+        if (!mIsPageMoving) {
+            hideOutlines();
+        }
     }
 
     private void onDropExternal(int x, int y, Object dragInfo,
@@ -1342,6 +1353,29 @@
         }
     }
 
+    @Override
+    public void onEnterScrollArea(int direction) {
+        mInScrollArea = true;
+        final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1);
+        if (0 <= screen && screen < getChildCount()) {
+            ((CellLayout) getChildAt(screen)).setHover(true);
+        }
+
+        if (mDragTargetLayout != null) {
+            mDragTargetLayout.onDragExit();
+            mDragTargetLayout = null;
+        }
+    }
+
+    @Override
+    public void onExitScrollArea() {
+        mInScrollArea = false;
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            ((CellLayout) getChildAt(i)).setHover(false);
+        }
+    }
+
     public Folder getFolderForTag(Object tag) {
         final int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {