Added config_springLoadWorkspace to enable/disable spring-loaded workspace.

- Added side hints back
- Only scale down icons if spring-loaded
- Only show App Info drop target if spring-loaded

Change-Id: I4b0dddccbe0e80b7ceb6b7266fc527f757744148
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 3d3d57d..b09723d 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -399,6 +399,10 @@
         mIsDragTarget = false;
     }
 
+    public boolean isDragTarget() {
+        return mIsDragTarget;
+    }
+
     void setIsDragOverlapping(boolean isDragOverlapping) {
         if (mIsDragOverlapping != isDragOverlapping) {
             mIsDragOverlapping = isDragOverlapping;
@@ -441,6 +445,10 @@
                 (ParcelableSparseArray) parcelable : new ParcelableSparseArray();
     }
 
+    public boolean getIsDragOverlapping() {
+        return mIsDragOverlapping;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (!mIsDragTarget) {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 1df1208..ee9e4f2 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -42,7 +42,6 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
@@ -950,7 +949,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return true;
+        return Workspace.IS_SPRING_LOADED;
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1eda66d..1af2c4f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1384,6 +1384,7 @@
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         dragController.setDragScoller(mWorkspace);
+        dragController.setScrollView(mDragLayer);
         dragController.setMoveTarget(mWorkspace);
         dragController.addDropTarget(mWorkspace);
         if (mSearchDropTargetBar != null) {
@@ -3488,8 +3489,10 @@
             mState = State.APPS_SPRING_LOADED;
         } else if (isWidgetsViewVisible()) {
             mState = State.WIDGETS_SPRING_LOADED;
-        } else {
+        } else if (Workspace.IS_SPRING_LOADED) {
             mState = State.WORKSPACE_SPRING_LOADED;
+        } else {
+            mState = State.WORKSPACE;
         }
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0d5bbc1..e86758a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -46,13 +46,10 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
 import android.widget.TextView;
 
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
@@ -93,6 +90,7 @@
         Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
     private static final String TAG = "Launcher.Workspace";
 
+    public static final boolean IS_SPRING_LOADED = true;
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
 
     private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
@@ -404,8 +402,9 @@
         mLauncher.onInteractionEnd();
     }
 
-    public float getSpringLoadedShrinkFactor() {
-        return mSpringLoadedShrinkFactor;
+    /** Returns a scale factor to apply to workspace icons when dragging them from the workspace. */
+    public float getDragShrinkFactor() {
+        return IS_SPRING_LOADED ? mSpringLoadedShrinkFactor : 1f;
     }
 
     /**
@@ -2146,7 +2145,9 @@
 
         b.recycle();
 
-        mLauncher.enterSpringLoadedDragMode();
+        if (IS_SPRING_LOADED) {
+            mLauncher.enterSpringLoadedDragMode();
+        }
     }
 
     public void beginExternalDragShared(View child, DragSource source) {
@@ -2196,7 +2197,9 @@
         // Recycle temporary bitmaps
         tmpB.recycle();
 
-        mLauncher.enterSpringLoadedDragMode();
+        if (IS_SPRING_LOADED) {
+            mLauncher.enterSpringLoadedDragMode();
+        }
     }
 
     public boolean transitionStateShouldAllowDrop() {
@@ -2635,6 +2638,10 @@
         CellLayout layout = getCurrentDropLayout();
         setCurrentDropLayout(layout);
         setCurrentDragOverlappingLayout(layout);
+
+        if (!workspaceInModalState() && !IS_SPRING_LOADED) {
+            mLauncher.getDragLayer().showPageHints();
+        }
     }
 
     @Override
@@ -2669,6 +2676,8 @@
         setCurrentDragOverlappingLayout(null);
 
         mSpringLoadedDragController.cancel();
+
+        mLauncher.getDragLayer().hidePageHints();
     }
 
     private void enfoceDragParity(String event, int update, int expectedValue) {
@@ -3552,7 +3561,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return true;
+        return IS_SPRING_LOADED;
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 13f83a7..84807cb 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -494,6 +494,8 @@
 
         // Start the drag
         mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false);
+        // Enter spring loaded mode
+        mLauncher.enterSpringLoadedDragMode();
 
         return false;
     }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d61117c..07775b6 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -35,7 +35,6 @@
 import android.view.ViewConfiguration;
 import android.view.inputmethod.InputMethodManager;
 
-import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
@@ -43,6 +42,7 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.util.Thunk;
 
@@ -81,6 +81,7 @@
     // temporaries to avoid gc thrash
     private Rect mRectTemp = new Rect();
     private final int[] mCoordinatesTemp = new int[2];
+    private final boolean mIsRtl;
 
     /**
      * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
@@ -97,6 +98,11 @@
     /** Y coordinate of the down event. */
     private int mMotionDownY;
 
+    /** the area at the edge of the screen that makes the workspace go left
+     *   or right while you're dragging.
+     */
+    private final int mScrollZone;
+
     private DropTarget.DragObject mDragObject;
 
     /** Who can receive drop events */
@@ -107,6 +113,9 @@
     /** The window token used as the parent for the DragView. */
     private IBinder mWindowToken;
 
+    /** The view that will be scrolled when dragging to the left and right edges of the screen. */
+    private View mScrollView;
+
     private View mMoveTarget;
 
     @Thunk DragScroller mDragScroller;
@@ -119,6 +128,7 @@
 
     @Thunk int mLastTouch[] = new int[2];
     @Thunk long mLastTouchUpTime = -1;
+    @Thunk int mDistanceSinceScroll = 0;
 
     private int mTmpPoint[] = new int[2];
     private Rect mDragLayerRect = new Rect();
@@ -148,15 +158,19 @@
 
     /**
      * Used to create a new DragLayer from XML.
+     *
+     * @param context The application's context.
      */
     public DragController(Launcher launcher) {
         Resources r = launcher.getResources();
         mLauncher = launcher;
         mHandler = new Handler();
+        mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone);
         mVelocityTracker = VelocityTracker.obtain();
 
         mFlingToDeleteThresholdVelocity =
                 r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
+        mIsRtl = Utilities.isRtl(r);
     }
 
     /**
@@ -231,7 +245,7 @@
 
         mDragObject = new DropTarget.DragObject();
 
-        float finalDragViewScale = mLauncher.getWorkspace().getSpringLoadedShrinkFactor();
+        float finalDragViewScale = mLauncher.getWorkspace().getDragShrinkFactor();
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, 0, 0, b.getWidth(), b.getHeight(),
                 initialDragViewScale, finalDragViewScale);
@@ -502,6 +516,7 @@
             mScrollState = SCROLL_OUTSIDE_ZONE;
             mScrollRunnable.setDirection(SCROLL_RIGHT);
             mDragScroller.onExitScrollArea();
+            mLauncher.getDragLayer().onExitScrollArea();
         }
     }
 
@@ -515,8 +530,11 @@
         mDragObject.y = coordinates[1];
         checkTouchMove(dropTarget);
 
+        // Check if we are hovering over the scroll areas
+        mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
         mLastTouch[0] = x;
         mLastTouch[1] = y;
+        checkScrollState(x, y);
     }
 
     public void forceTouchMove() {
@@ -544,6 +562,36 @@
         mLastDropTarget = dropTarget;
     }
 
+    @Thunk void checkScrollState(int x, int y) {
+        final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
+        final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
+        final DragLayer dragLayer = mLauncher.getDragLayer();
+        final int forwardDirection = mIsRtl ? SCROLL_RIGHT : SCROLL_LEFT;
+        final int backwardsDirection = mIsRtl ? SCROLL_LEFT : SCROLL_RIGHT;
+
+        if (x < mScrollZone) {
+            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+                mScrollState = SCROLL_WAITING_IN_ZONE;
+                if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
+                    dragLayer.onEnterScrollArea(forwardDirection);
+                    mScrollRunnable.setDirection(forwardDirection);
+                    mHandler.postDelayed(mScrollRunnable, delay);
+                }
+            }
+        } else if (x > mScrollView.getWidth() - mScrollZone) {
+            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+                mScrollState = SCROLL_WAITING_IN_ZONE;
+                if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
+                    dragLayer.onEnterScrollArea(backwardsDirection);
+                    mScrollRunnable.setDirection(backwardsDirection);
+                    mHandler.postDelayed(mScrollRunnable, delay);
+                }
+            }
+        } else {
+            clearScrollRunnable();
+        }
+    }
+
     /**
      * Call this from a drag source view.
      */
@@ -565,6 +613,13 @@
                 // Remember where the motion event started
                 mMotionDownX = dragLayerX;
                 mMotionDownY = dragLayerY;
+
+                if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
+                    mScrollState = SCROLL_WAITING_IN_ZONE;
+                    mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+                } else {
+                    mScrollState = SCROLL_OUTSIDE_ZONE;
+                }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
@@ -757,6 +812,13 @@
         }
     }
 
+    /**
+     * Set which view scrolls for touch events near the edge of the screen.
+     */
+    public void setScrollView(View v) {
+        mScrollView = v;
+    }
+
     private class ScrollRunnable implements Runnable {
         private int mDirection;
 
@@ -771,7 +833,14 @@
                     mDragScroller.scrollRight();
                 }
                 mScrollState = SCROLL_OUTSIDE_ZONE;
+                mDistanceSinceScroll = 0;
                 mDragScroller.onExitScrollArea();
+                mLauncher.getDragLayer().onExitScrollArea();
+
+                if (isDragging()) {
+                    // Check the scroll again so that we can requeue the scroller if necessary
+                    checkScrollState(mLastTouch[0], mLastTouch[1]);
+                }
             }
         }
 
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 1fcb523..ded7fae 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -102,6 +102,15 @@
     // Darkening scrim
     private float mBackgroundAlpha = 0;
 
+    // Related to adjacent page hints
+    private final Rect mScrollChildPosition = new Rect();
+    private boolean mInScrollArea;
+    private boolean mShowPageHints;
+    private Drawable mLeftHoverDrawable;
+    private Drawable mRightHoverDrawable;
+    private Drawable mLeftHoverDrawableActive;
+    private Drawable mRightHoverDrawableActive;
+
     private boolean mBlockTouches = false;
 
     /**
@@ -117,7 +126,12 @@
         setMotionEventSplittingEnabled(false);
         setChildrenDrawingOrderEnabled(true);
 
-        mIsRtl = Utilities.isRtl(getResources());
+        final Resources res = getResources();
+        mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
+        mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
+        mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
+        mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
+        mIsRtl = Utilities.isRtl(res);
     }
 
     public void setup(Launcher launcher, DragController controller) {
@@ -883,6 +897,29 @@
         }
     }
 
+    void onEnterScrollArea(int direction) {
+        mInScrollArea = true;
+        invalidate();
+    }
+
+    void onExitScrollArea() {
+        mInScrollArea = false;
+        invalidate();
+    }
+
+    public void showPageHints() {
+        mShowPageHints = true;
+        Workspace workspace = mLauncher.getWorkspace();
+        getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()),
+                mScrollChildPosition);
+        invalidate();
+    }
+
+    public void hidePageHints() {
+        mShowPageHints = false;
+        invalidate();
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the background below children.
@@ -894,6 +931,41 @@
         super.dispatchDraw(canvas);
     }
 
+    private void drawPageHints(Canvas canvas) {
+        if (mShowPageHints) {
+            Workspace workspace = mLauncher.getWorkspace();
+            int width = getMeasuredWidth();
+            int page = workspace.getNextPage();
+            CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1);
+            CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1);
+
+            if (leftPage != null && leftPage.isDragTarget()) {
+                Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
+                        mLeftHoverDrawableActive : mLeftHoverDrawable;
+                left.setBounds(0, mScrollChildPosition.top,
+                        left.getIntrinsicWidth(), mScrollChildPosition.bottom);
+                left.draw(canvas);
+            }
+            if (rightPage != null && rightPage.isDragTarget()) {
+                Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
+                        mRightHoverDrawableActive : mRightHoverDrawable;
+                right.setBounds(width - right.getIntrinsicWidth(),
+                        mScrollChildPosition.top, width, mScrollChildPosition.bottom);
+                right.draw(canvas);
+            }
+        }
+    }
+
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        boolean ret = super.drawChild(canvas, child, drawingTime);
+
+        // We want to draw the page hints above the workspace, but below the drag view.
+        if (child instanceof Workspace) {
+            drawPageHints(canvas);
+        }
+        return ret;
+    }
+
     public void setBackgroundAlpha(float alpha) {
         if (alpha != mBackgroundAlpha) {
             mBackgroundAlpha = alpha;