Adding overview mode for reordering, widget adding and wallpaper switching

Change-Id: I082ba0b90ca4b3fbba32e8dfdec8ba79486d841c
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f990d25..09881b6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -79,6 +79,7 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -236,6 +237,8 @@
     private FolderInfo mFolderInfo;
 
     private Hotseat mHotseat;
+    private View mOverviewPanel;
+
     private View mAllAppsButton;
 
     private SearchDropTargetBar mSearchDropTargetBar;
@@ -1065,6 +1068,20 @@
             mHotseat.setup(this);
         }
 
+        mOverviewPanel = findViewById(R.id.overview_panel);
+        findViewById(R.id.widget_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View arg0) {
+                showAllApps(true);
+            }
+        });
+        findViewById(R.id.wallpaper_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View arg0) {
+                startWallpaper();
+            }
+        });
+
         // Setup the workspace
         mWorkspace.setHapticFeedbackEnabled(false);
         mWorkspace.setOnLongClickListener(this);
@@ -1560,6 +1577,9 @@
                     // otherwise, just wait until onResume to set the state back to Workspace
                     if (alreadyOnHome) {
                         showWorkspace(true);
+                        if (mWorkspace.isInOverviewMode()) {
+                            mWorkspace.exitOverviewMode();
+                        }
                     } else {
                         mOnResumeState = State.WORKSPACE;
                     }
@@ -2050,6 +2070,8 @@
     public void onBackPressed() {
         if (isAllAppsVisible()) {
             showWorkspace(true);
+        } else if (mWorkspace.isInOverviewMode()) {
+            mWorkspace.exitOverviewMode();
         } else if (mWorkspace.getOpenFolder() != null) {
             Folder openFolder = mWorkspace.getOpenFolder();
             if (openFolder.isEditingName()) {
@@ -2090,6 +2112,19 @@
             return;
         }
 
+        if (v instanceof PageIndicator) {
+            if (!mWorkspace.isInOverviewMode()) {
+                mWorkspace.enterOverviewMode();
+            }
+            return;
+        }
+
+        if (v instanceof CellLayout) {
+            if (mWorkspace.isInOverviewMode()) {
+                mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v));
+            }
+        }
+
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
             // Open shortcut
@@ -2138,11 +2173,6 @@
     }
 
     public boolean onTouch(View v, MotionEvent event) {
-        // this is an intercepted event being forwarded from mWorkspace;
-        // clicking anywhere on the workspace causes the customization drawer to slide down
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            showWorkspace(true);
-        }
         return false;
     }
 
@@ -2510,10 +2540,10 @@
                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                 // Disabling reordering until we sort out some issues.
-                if (mWorkspace.getIdForScreen((CellLayout) v) >= 0) {
-                    mWorkspace.startReordering();
+                if (mWorkspace.isInOverviewMode()) {
+                    mWorkspace.startReordering(v);
                 } else {
-                    startWallpaper();
+                    mWorkspace.enterOverviewMode();
                 }
             } else {
                 if (!(itemUnderLongClick instanceof Folder)) {
@@ -2532,6 +2562,9 @@
     Hotseat getHotseat() {
         return mHotseat;
     }
+    View getOverviewPanel() {
+        return mOverviewPanel;
+    }
     SearchDropTargetBar getSearchBar() {
         return mSearchDropTargetBar;
     }
@@ -2852,11 +2885,10 @@
         final View fromView = mAppsCustomizeTabHost;
         final View toView = mWorkspace;
         Animator workspaceAnim = null;
-
         if (toState == State.WORKSPACE) {
             int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
             workspaceAnim = mWorkspace.getChangeStateAnimation(
-                    Workspace.State.NORMAL, animated, stagger);
+                    Workspace.State.NORMAL, animated, stagger, -1);
         } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
             workspaceAnim = mWorkspace.getChangeStateAnimation(
                     Workspace.State.SPRING_LOADED, animated);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index bb596a7..ccf2156 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -88,9 +88,13 @@
 
     // We are disabling touch interaction of the widget region for factory ROM.
     private static final boolean DISABLE_TOUCH_INTERACTION = false;
-    private static final boolean DISABLE_TOUCH_SIDE_PAGES = false;
+    private static final boolean DISABLE_TOUCH_SIDE_PAGES = true;
     private static final boolean DISABLE_FLING_TO_DELETE = true;
 
+    private boolean mFreeScroll = false;
+    private int mFreeScrollMinScrollX = -1;
+    private int mFreeScrollMaxScrollX = -1;
+
     static final int AUTOMATIC_PAGE_SPACING = -1;
 
     protected int mFlingThresholdVelocity;
@@ -117,6 +121,7 @@
     private float mDownMotionX;
     private float mDownMotionY;
     private float mDownScrollX;
+    private float mDragViewBaselineLeft;
     protected float mLastMotionX;
     protected float mLastMotionXRemainder;
     protected float mLastMotionY;
@@ -205,9 +210,7 @@
     private int REORDERING_DROP_REPOSITION_DURATION = 200;
     protected int REORDERING_REORDER_REPOSITION_DURATION = 300;
     protected int REORDERING_ZOOM_IN_OUT_DURATION = 250;
-    private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300;
-    private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
-    private long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150;
+    private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80;
     private float mMinScale = 1f;
     protected View mDragView;
     protected AnimatorSet mZoomInOutAnim;
@@ -223,11 +226,6 @@
     private int mPostReorderingPreZoomInRemainingAnimationCount;
     private Runnable mPostReorderingPreZoomInRunnable;
 
-    // Edge swiping
-    private boolean mOnlyAllowEdgeSwipes = false;
-    private boolean mDownEventOnEdge = false;
-    private int mEdgeSwipeRegionSize = 0;
-
     // Convenience/caching
     private Matrix mTmpInvMatrix = new Matrix();
     private float[] mTmpPoint = new float[2];
@@ -332,7 +330,9 @@
             for (int i = 0; i < getChildCount(); ++i) {
                 markers.add(getPageIndicatorMarker(i));
             }
+
             mPageIndicator.addMarkers(markers);
+            mPageIndicator.setOnClickListener((Launcher) getContext());
         }
     }
 
@@ -363,12 +363,16 @@
     }
 
     void updateDragViewTranslationDuringDrag() {
-        float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
-        float y = mLastMotionY - mDownMotionY;
-        mDragView.setTranslationX(x);
-        mDragView.setTranslationY(y);
+        if (mDragView != null) {
+            float x = (mLastMotionX - mDownMotionX) + (getScrollX() - mDownScrollX) +
+                    (mDragViewBaselineLeft - mDragView.getLeft());
+            float y = mLastMotionY - mDownMotionY;
+            mDragView.setTranslationX(x);
+            mDragView.setTranslationY(y);
 
-        if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
+            if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): "
+                    + x + ", " + y);
+        }
     }
 
     public void setMinScale(float f) {
@@ -580,6 +584,12 @@
 
     @Override
     public void scrollTo(int x, int y) {
+        // In free scroll mode, we clamp the scrollX
+        if (mFreeScroll) {
+            x = Math.min(x, mFreeScrollMaxScrollX);
+            x = Math.max(x, mFreeScrollMinScrollX);
+        }
+
         final boolean isRtl = isLayoutRtl();
         mUnboundedScrollX = x;
 
@@ -627,7 +637,9 @@
             if (getScrollX() != mScroller.getCurrX()
                 || getScrollY() != mScroller.getCurrY()
                 || mOverScrollX != mScroller.getCurrX()) {
-                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+                float scaleX = mFreeScroll ? getScaleX() : 1f;
+                int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX));
+                scrollTo(scrollX, mScroller.getCurrY());
             }
             invalidate();
             return true;
@@ -899,6 +911,10 @@
             }
         }
         mChildCountOnLastLayout = getChildCount();
+
+        if (isReordering(true)) {
+            updateDragViewTranslationDuringDrag();
+        }
     }
 
     protected void screenScrolled(int screenCenter) {
@@ -991,7 +1007,7 @@
         return offset;
     }
 
-    void getReorderablePages(int[] range) {
+    protected void getOverviewModePages(int[] range) {
         range[0] = 0;
         range[1] = getChildCount() - 1;
     }
@@ -1217,17 +1233,6 @@
         return mTmpRect.contains(x, y);
     }
 
-    /** Returns whether x and y originated within the current page view bounds */
-    private boolean isTouchPointInCurrentPage(int x, int y) {
-        View v = getPageAt(getCurrentPage());
-        if (v != null) {
-            mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()),
-                    v.getBottom());
-            return mTmpRect.contains(x, y);
-        }
-        return false;
-    }
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DISABLE_TOUCH_INTERACTION) {
@@ -1288,13 +1293,6 @@
                 mTotalMotionX = 0;
                 mActivePointerId = ev.getPointerId(0);
 
-                // Determine if the down event is within the threshold to be an edge swipe
-                int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
-                int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
-                if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
-                    mDownEventOnEdge = true;
-                }
-
                 /*
                  * If being flinged and user touches the screen, initiate drag;
                  * otherwise don't.  mScroller.isFinished should be false when
@@ -1332,10 +1330,6 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 resetTouchState();
-                // Just intercept the touch event on up if we tap outside the strict viewport
-                if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) {
-                    return true;
-                }
                 break;
 
             case MotionEvent.ACTION_POINTER_UP:
@@ -1369,10 +1363,6 @@
         final float y = ev.getY(pointerIndex);
         if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;
 
-        // If we're only allowing edge swipes, we break out early if the down event wasn't
-        // at the edge.
-        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return;
-
         final int xDiff = (int) Math.abs(x - mLastMotionX);
         final int yDiff = (int) Math.abs(y - mLastMotionY);
 
@@ -1509,6 +1499,75 @@
         return OVERSCROLL_DAMP_FACTOR * f;
     }
 
+    protected void enableFreeScroll() {
+        setEnableFreeScroll(true, -1);
+    }
+
+    protected void disableFreeScroll(int snapPage) {
+        setEnableFreeScroll(false, snapPage);
+    }
+
+    private void setEnableFreeScroll(boolean freeScroll, int snapPage) {
+        mFreeScroll = freeScroll;
+
+        if (snapPage == -1) {
+            snapPage = getPageNearestToCenterOfScreen();
+        }
+
+        getOverviewModePages(mTempVisiblePagesRange);
+        if (!mFreeScroll) {
+            snapToPage(snapPage);
+
+            for (int i = 0; i < getPageCount(); ++i) {
+                if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+                    getPageAt(i).setAlpha(1f);
+                }
+            }
+        } else {
+            mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
+            mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
+
+            for (int i = 0; i < getPageCount(); ++i) {
+                if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
+                    getPageAt(i).setAlpha(0f);
+                }
+            }
+
+            if (getCurrentPage() < mTempVisiblePagesRange[0]) {
+                setCurrentPage(mTempVisiblePagesRange[0]);
+            } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
+                setCurrentPage(mTempVisiblePagesRange[1]);
+            }
+        }
+
+        setEnableOverscroll(!freeScroll);
+    }
+
+    private void setEnableOverscroll(boolean enable) {
+        mAllowOverScroll = enable;
+    }
+
+    int getNearestHoverOverPageIndex() {
+        if (mDragView != null) {
+            int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
+                    + mDragView.getTranslationX());
+            getOverviewModePages(mTempVisiblePagesRange);
+            int minDistance = Integer.MAX_VALUE;
+            int minIndex = indexOfChild(mDragView);
+            for (int i = mTempVisiblePagesRange[0]; i <= mTempVisiblePagesRange[1]; i++) {
+                View page = getPageAt(i);
+                int pageX = (int) (page.getLeft() + page.getMeasuredWidth() / 2);
+                int d = Math.abs(dragX - pageX);
+                if (d < minDistance) {
+                    minIndex = i;
+                    minDistance = d;
+                }
+            }
+            return minIndex;
+        }
+        return -1;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (DISABLE_TOUCH_INTERACTION) {
@@ -1543,13 +1602,6 @@
             mTotalMotionX = 0;
             mActivePointerId = ev.getPointerId(0);
 
-            // Determine if the down event is within the threshold to be an edge swipe
-            int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
-            int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
-            if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
-                mDownEventOnEdge = true;
-            }
-
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 pageBeginMoving();
             }
@@ -1598,38 +1650,23 @@
 
                 // Find the closest page to the touch point
                 final int dragViewIndex = indexOfChild(mDragView);
-                int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
-                    getViewportWidth());
-                int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0]
-                        + bufferSize);
-                int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0]
-                        - bufferSize);
 
                 // Change the drag view if we are hovering over the drop target
                 boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget(
                         (int) mParentDownMotionX, (int) mParentDownMotionY);
                 setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete);
 
-                if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
-                if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
                 if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
                 if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
                 if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
                 if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
 
-                float parentX = mParentDownMotionX;
-                int pageIndexToSnapTo = -1;
-                if (parentX < leftBufferEdge && dragViewIndex > 0) {
-                    pageIndexToSnapTo = dragViewIndex - 1;
-                } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
-                    pageIndexToSnapTo = dragViewIndex + 1;
-                }
-
-                final int pageUnderPointIndex = pageIndexToSnapTo;
-                if (pageUnderPointIndex > -1 && !isHoveringOverDelete) {
+                final int pageUnderPointIndex = getNearestHoverOverPageIndex();
+                if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView) &&
+                        !isHoveringOverDelete) {
                     mTempVisiblePagesRange[0] = 0;
                     mTempVisiblePagesRange[1] = getPageCount() - 1;
-                    getReorderablePages(mTempVisiblePagesRange);
+                    getOverviewModePages(mTempVisiblePagesRange);
                     if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
                             pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
                             pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
@@ -1637,10 +1674,6 @@
                         mSidePageHoverRunnable = new Runnable() {
                             @Override
                             public void run() {
-                                // Update the down scroll position to account for the fact that the
-                                // current page is moved
-                                mDownScrollX = getScrollForPage(pageUnderPointIndex);
-
                                 // Setup the scroll to the correct page before we swap the views
                                 snapToPage(pageUnderPointIndex);
 
@@ -1713,42 +1746,56 @@
                 boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
                         Math.abs(velocityX) > mFlingThresholdVelocity;
 
-                // In the case that the page is moved far to one direction and then is flung
-                // in the opposite direction, we use a threshold to determine whether we should
-                // just return to the starting page, or if we should skip one further.
-                boolean returnToOriginalPage = false;
-                if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
-                        Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
-                    returnToOriginalPage = true;
-                }
+                if (!mFreeScroll) {
+                    // In the case that the page is moved far to one direction and then is flung
+                    // in the opposite direction, we use a threshold to determine whether we should
+                    // just return to the starting page, or if we should skip one further.
+                    boolean returnToOriginalPage = false;
+                    if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
+                            Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
+                        returnToOriginalPage = true;
+                    }
 
-                int finalPage;
-                // We give flings precedence over large moves, which is why we short-circuit our
-                // test for a large move if a fling has been registered. That is, a large
-                // move to the left and fling to the right will register as a fling to the right.
-                final boolean isRtl = isLayoutRtl();
-                boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
-                boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
-                if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
-                        (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
-                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
-                    snapToPageWithVelocity(finalPage, velocityX);
-                } else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
-                        (isFling && isVelocityXLeft)) &&
-                        mCurrentPage < getChildCount() - 1) {
-                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
-                    snapToPageWithVelocity(finalPage, velocityX);
+                    int finalPage;
+                    // We give flings precedence over large moves, which is why we short-circuit our
+                    // test for a large move if a fling has been registered. That is, a large
+                    // move to the left and fling to the right will register as a fling to the right.
+                    final boolean isRtl = isLayoutRtl();
+                    boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
+                    boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
+                    if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
+                            (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
+                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+                        snapToPageWithVelocity(finalPage, velocityX);
+                    } else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
+                            (isFling && isVelocityXLeft)) &&
+                            mCurrentPage < getChildCount() - 1) {
+                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+                        snapToPageWithVelocity(finalPage, velocityX);
+                    } else {
+                        snapToDestination();
+                    }            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+                    // at this point we have not moved beyond the touch slop
+                    // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+                    // we can just page
+                    int nextPage = Math.max(0, mCurrentPage - 1);
+                    if (nextPage != mCurrentPage) {
+                        snapToPage(nextPage);
+                    } else {
+                        snapToDestination();
+                    }
                 } else {
-                    snapToDestination();
-                }            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
-                // at this point we have not moved beyond the touch slop
-                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
-                // we can just page
-                int nextPage = Math.max(0, mCurrentPage - 1);
-                if (nextPage != mCurrentPage) {
-                    snapToPage(nextPage);
-                } else {
-                    snapToDestination();
+                    if (!mScroller.isFinished()) {
+                        mScroller.abortAnimation();
+                    }
+
+                    float scaleX = getScaleX();
+                    int vX = (int) (-velocityX * scaleX);
+                    int initialScrollX = (int) (getScrollX() * scaleX);
+
+                    mScroller.fling(initialScrollX,
+                            getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
+                    invalidate();
                 }
             } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
                 // at this point we have not moved beyond the touch slop
@@ -1803,6 +1850,7 @@
 
         case MotionEvent.ACTION_POINTER_UP:
             onSecondaryPointerUp(ev);
+            releaseVelocityTracker();
             break;
         }
 
@@ -1819,7 +1867,6 @@
         endReordering();
         mTouchState = TOUCH_STATE_REST;
         mActivePointerId = INVALID_POINTER;
-        mDownEventOnEdge = false;
     }
 
     protected void onUnhandledTap(MotionEvent ev) {}
@@ -1864,6 +1911,7 @@
 
     private void releaseVelocityTracker() {
         if (mVelocityTracker != null) {
+            mVelocityTracker.clear();
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
@@ -2031,7 +2079,9 @@
             duration = Math.abs(delta);
         }
 
-        if (!mScroller.isFinished()) mScroller.abortAnimation();
+        if (!mScroller.isFinished()) {
+            mScroller.abortAnimation();
+        }
         mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
 
         notifyPageSwitchListener();
@@ -2231,7 +2281,9 @@
             anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
             anim.playTogether(
                     ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
-                    ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
+                    ObjectAnimator.ofFloat(mDragView, "translationY", 0f),
+                    ObjectAnimator.ofFloat(mDragView, "scaleX", 1f),
+                    ObjectAnimator.ofFloat(mDragView, "scaleY", 1f));
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -2242,59 +2294,11 @@
         }
     }
 
-    // "Zooms out" the PagedView to reveal more side pages
-    protected boolean zoomOut() {
-        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
-            mZoomInOutAnim.cancel();
-        }
-
-        if (!(getScaleX() < 1f || getScaleY() < 1f)) {
-            mZoomInOutAnim = new AnimatorSet();
-            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
-            mZoomInOutAnim.playTogether(
-                    ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
-                    ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
-            mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Show the delete drop target
-                    if (mDeleteDropTarget != null) {
-                        mDeleteDropTarget.setVisibility(View.VISIBLE);
-                        mDeleteDropTarget.animate().alpha(1f)
-                            .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
-                            .setListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationStart(Animator animation) {
-                                    mDeleteDropTarget.setAlpha(0f);
-                                }
-                            });
-                    }
-                }
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    // Update the visible pages
-                    invalidate();
-                }
-            });
-            mZoomInOutAnim.start();
-            return true;
-        }
-        return false;
-    }
-
     protected void onStartReordering() {
         // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
         mTouchState = TOUCH_STATE_REORDERING;
         mIsReordering = true;
 
-        // Mark all the non-widget pages as invisible
-        getReorderablePages(mTempVisiblePagesRange);
-        for (int i = 0; i < getPageCount(); ++i) {
-            if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
-                getPageAt(i).setAlpha(0f);
-            }
-        }
-
         // We must invalidate to trigger a redraw to update the layers such that the drag view
         // is always drawn on top
         invalidate();
@@ -2312,32 +2316,24 @@
 
     protected void onEndReordering() {
         mIsReordering = false;
-
-        // Mark all the non-widget pages as visible again
-        getReorderablePages(mTempVisiblePagesRange);
-        for (int i = 0; i < getPageCount(); ++i) {
-            if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) {
-                getPageAt(i).setAlpha(1f);
-            }
-        }
     }
 
-    public boolean startReordering() {
-        int dragViewIndex = getPageNearestToCenterOfScreen();
+    public boolean startReordering(View v) {
+        int dragViewIndex = indexOfChild(v);//getPageNearestToCenterOfScreen();
         mTempVisiblePagesRange[0] = 0;
         mTempVisiblePagesRange[1] = getPageCount() - 1;
-        getReorderablePages(mTempVisiblePagesRange);
+        getOverviewModePages(mTempVisiblePagesRange);
         mReorderingStarted = true;
 
         // Check if we are within the reordering range
         if (mTempVisiblePagesRange[0] <= dragViewIndex &&
-                dragViewIndex <= mTempVisiblePagesRange[1]) {
-            if (zoomOut()) {
-                // Find the drag view under the pointer
-                mDragView = getChildAt(dragViewIndex);
-
-                onStartReordering();
-            }
+            dragViewIndex <= mTempVisiblePagesRange[1]) {
+            // Find the drag view under the pointer
+            mDragView = getChildAt(dragViewIndex);
+            mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start();
+            mDragViewBaselineLeft = mDragView.getLeft();
+            disableFreeScroll(-1);
+            onStartReordering();
             return true;
         }
         return false;
@@ -2367,7 +2363,8 @@
         if (!mDeferringForDelete) {
             mPostReorderingPreZoomInRunnable = new Runnable() {
                 public void run() {
-                    zoomIn(onCompleteRunnable);
+                    onCompleteRunnable.run();
+                    enableFreeScroll();
                 };
             };
 
@@ -2382,56 +2379,6 @@
         }
     }
 
-    // "Zooms in" the PagedView to highlight the current page
-    protected boolean zoomIn(final Runnable onCompleteRunnable) {
-        if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
-            mZoomInOutAnim.cancel();
-        }
-        if (getScaleX() < 1f || getScaleY() < 1f) {
-            mZoomInOutAnim = new AnimatorSet();
-            mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
-            mZoomInOutAnim.playTogether(
-                    ObjectAnimator.ofFloat(this, "scaleX", 1f),
-                    ObjectAnimator.ofFloat(this, "scaleY", 1f));
-            mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    // Hide the delete drop target
-                    if (mDeleteDropTarget != null) {
-                        mDeleteDropTarget.animate().alpha(0f)
-                            .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION)
-                            .setListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    mDeleteDropTarget.setVisibility(View.GONE);
-                                }
-                            });
-                    }
-                }
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    mDragView = null;
-                }
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mDragView = null;
-                    if (onCompleteRunnable != null) {
-                        onCompleteRunnable.run();
-                    }
-                    // Update the visible pages
-                    invalidate();
-                }
-            });
-            mZoomInOutAnim.start();
-            return true;
-        } else {
-            if (onCompleteRunnable != null) {
-                onCompleteRunnable.run();
-            }
-        }
-        return false;
-    }
-
     /*
      * Flinging to delete - IN PROGRESS
      */
@@ -2507,7 +2454,7 @@
                 // in the layout)
                 // NOTE: We can make an assumption here because we have side-bound pages that we
                 //       will always have pages to animate in from the left
-                getReorderablePages(mTempVisiblePagesRange);
+                getOverviewModePages(mTempVisiblePagesRange);
                 boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
                 boolean slideFromLeft = (isLastWidgetPage ||
                         dragViewIndex > mTempVisiblePagesRange[0]);
@@ -2568,15 +2515,9 @@
                 slideAnimations.addListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
-                        final Runnable onCompleteRunnable = new Runnable() {
-                            @Override
-                            public void run() {
-                                mDeferringForDelete = false;
-                                onEndReordering();
-                                onRemoveViewAnimationCompleted();
-                            }
-                        };
-                        zoomIn(onCompleteRunnable);
+                        mDeferringForDelete = false;
+                        onEndReordering();
+                        onRemoveViewAnimationCompleted();
                     }
                 });
                 slideAnimations.start();
@@ -2748,4 +2689,4 @@
     public boolean onHoverEvent(android.view.MotionEvent event) {
         return true;
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6989c9a..4e95f09 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -17,6 +17,8 @@
 package com.android.launcher3;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
@@ -87,6 +89,8 @@
     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
     private static final int FLING_THRESHOLD_VELOCITY = 500;
 
+    private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
+
     // These animators are used to fade the children's outlines
     private ObjectAnimator mChildrenOutlineFadeInAnimation;
     private ObjectAnimator mChildrenOutlineFadeOutAnimation;
@@ -160,6 +164,7 @@
 
     private SpringLoadedDragController mSpringLoadedDragController;
     private float mSpringLoadedShrinkFactor;
+    private float mOverviewModeShrinkFactor;
 
     private static final int DEFAULT_CELL_COUNT_X = 4;
     private static final int DEFAULT_CELL_COUNT_Y = 4;
@@ -167,7 +172,7 @@
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
 
-    enum State { NORMAL, SPRING_LOADED, SMALL };
+    enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW};
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
@@ -331,13 +336,14 @@
 
         mSpringLoadedShrinkFactor =
             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
+        mOverviewModeShrinkFactor =
+                res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100.0f;
         mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
 
         // if the value is manually specified, use that instead
         cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
         cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
         mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
-
         a.recycle();
 
         setOnHierarchyChangeListener(this);
@@ -518,6 +524,7 @@
                 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
 
         newScreen.setOnLongClickListener(mLongClickListener);
+        newScreen.setOnClickListener(mLauncher);
         mWorkspaceScreens.put(screenId, newScreen);
         mScreenOrder.add(insertIndex, screenId);
         addView(newScreen, insertIndex);
@@ -761,7 +768,8 @@
      */
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        return (isSmall() || !isFinishedSwitchingState());
+        return (isSmall() || !isFinishedSwitchingState())
+                || (!isSmall() && indexOfChild(v) != mCurrentPage);
     }
 
     public boolean isSwitchingState() {
@@ -810,11 +818,6 @@
                 }
             }
         }
-
-        if (mLauncher != null && mLauncher.onTouch(this, ev)) {
-            return true;
-        }
-
         return super.onInterceptTouchEvent(ev);
     }
 
@@ -843,7 +846,6 @@
 
     @Override
     protected void determineScrollingStart(MotionEvent ev) {
-        if (isSmall()) return;
         if (!isFinishedSwitchingState()) return;
 
         float deltaX = Math.abs(ev.getX() - mXDown);
@@ -1315,11 +1317,9 @@
     }
 
     private void updateStateForCustomContent(int screenCenter) {
-        if (hasCustomContent()) {
+        if (hasCustomContent() && !isSmall() && !isSwitchingState()) {
             int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
-
             int scrollDelta = getScrollForPage(index + 1) - getScrollX();
-
             float progress = (1.0f * scrollDelta) /
                     (getScrollForPage(index + 1) - getScrollForPage(index));
             progress = Math.max(0, progress);
@@ -1338,7 +1338,14 @@
                 mLauncher.getHotseat().setAlpha(1 - progress);
             }
             if (getPageIndicator() != null) {
-                getPageIndicator().setAlpha(1 - progress);
+                final float alpha = 1 - progress;
+                final View pi = getPageIndicator();
+                getPageIndicator().setAlpha(alpha);
+                if (alpha < ALPHA_CUTOFF_THRESHOLD && pi.getVisibility() != INVISIBLE) {
+                    pi.setVisibility(INVISIBLE);
+                } else if (alpha > ALPHA_CUTOFF_THRESHOLD && pi.getVisibility() != VISIBLE) {
+                    pi.setVisibility(VISIBLE);
+                }
             }
         }
     }
@@ -1474,7 +1481,7 @@
     }
 
     public boolean isSmall() {
-        return mState == State.SMALL || mState == State.SPRING_LOADED;
+        return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW;
     }
 
     void enableChildrenCache(int fromPage, int toPage) {
@@ -1508,9 +1515,8 @@
         }
     }
 
-
     private void updateChildrenLayersEnabled(boolean force) {
-        boolean small = mState == State.SMALL || mIsSwitchingState;
+        boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState;
         boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
@@ -1682,10 +1688,11 @@
     }
 
     Animator getChangeStateAnimation(final State state, boolean animated) {
-        return getChangeStateAnimation(state, animated, 0);
+        return getChangeStateAnimation(state, animated, 0, -1);
     }
 
-    void getReorderablePages(int[] range) {
+    @Override
+    protected void getOverviewModePages(int[] range) {
         int count = mScreenOrder.size();
 
         int start = -1;
@@ -1734,7 +1741,43 @@
         setLayoutTransition(mLayoutTransition);
     }
 
-    Animator getChangeStateAnimation(final State state, boolean animated, int delay) {
+    public boolean isInOverviewMode() {
+        return mState == State.OVERVIEW;
+    }
+
+    public void enterOverviewMode() {
+        enableOverviewMode(true, -1);
+    }
+
+    public void exitOverviewMode() {
+        exitOverviewMode(-1);
+    }
+
+    public void exitOverviewMode(int snapPage) {
+        enableOverviewMode(false, snapPage);
+    }
+
+    private void enableOverviewMode(boolean enable, int snapPage) {
+        State finalState = Workspace.State.OVERVIEW;
+        if (!enable) {
+            finalState = Workspace.State.NORMAL;
+        }
+
+        Animator workspaceAnim = getChangeStateAnimation(finalState, true, 0, snapPage);
+        workspaceAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator arg0) {
+                mIsSwitchingState = false;
+            }
+            @Override
+            public void onAnimationStart(Animator arg0) {
+                mIsSwitchingState = true;
+            }
+        });
+        workspaceAnim.start();
+    }
+
+    Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
         if (mState == state) {
             return null;
         }
@@ -1744,23 +1787,37 @@
 
         AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null;
 
-        // Stop any scrolling, move to the current page right away
-        setCurrentPage(getNextPage());
-
         final State oldState = mState;
         final boolean oldStateIsNormal = (oldState == State.NORMAL);
         final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
         final boolean oldStateIsSmall = (oldState == State.SMALL);
+        final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
         mState = state;
         final boolean stateIsNormal = (state == State.NORMAL);
         final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
         final boolean stateIsSmall = (state == State.SMALL);
+        final boolean stateIsOverview = (state == State.OVERVIEW);
         float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
+        float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
+        float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
+
         boolean zoomIn = true;
         mNewScale = 1.0f;
 
+        if (oldStateIsOverview) {
+            disableFreeScroll(snapPage);
+        } else if (stateIsOverview) {
+            enableFreeScroll();
+        }
+
         if (state != State.NORMAL) {
-            mNewScale = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
+            if (stateIsSpringLoaded) {
+                mNewScale = mSpringLoadedShrinkFactor;
+            } else if (stateIsOverview) {
+                mNewScale = mOverviewModeShrinkFactor;
+            } else if (stateIsSmall){
+                mNewScale = mOverviewModeShrinkFactor - 0.1f;
+            }
             if (oldStateIsNormal && stateIsSmall) {
                 zoomIn = false;
                 updateChildrenLayersEnabled(false);
@@ -1844,7 +1901,24 @@
                     }
                 }
             }
+            ObjectAnimator hotseatAlpha = ObjectAnimator.ofFloat(mLauncher.getHotseat(), "alpha",
+                    finalHotseatAndPageIndicatorAlpha);
+            ObjectAnimator pageIndicatorAlpha = ObjectAnimator.ofFloat(getPageIndicator(), "alpha",
+                    finalHotseatAndPageIndicatorAlpha);
+            ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
+                    "alpha", finalOverviewPanelAlpha);
+            overviewPanelAlpha.addUpdateListener(new AlphaUpdateListener(
+                    mLauncher.getOverviewPanel()));
+            hotseatAlpha.addUpdateListener(new AlphaUpdateListener(mLauncher.getHotseat()));
+            pageIndicatorAlpha.addUpdateListener(new AlphaUpdateListener(getPageIndicator()));
+            anim.play(overviewPanelAlpha);
+            anim.play(hotseatAlpha);
+            anim.play(pageIndicatorAlpha);
             anim.setStartDelay(delay);
+        } else {
+            mLauncher.getOverviewPanel().setAlpha(finalOverviewPanelAlpha);
+            mLauncher.getHotseat().setAlpha(finalHotseatAndPageIndicatorAlpha);
+            getPageIndicator().setAlpha(finalHotseatAndPageIndicatorAlpha);
         }
 
         if (stateIsSpringLoaded) {
@@ -1860,6 +1934,24 @@
         return anim;
     }
 
+    class AlphaUpdateListener implements AnimatorUpdateListener {
+        View view;
+        public AlphaUpdateListener(View v) {
+            view = v;
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator arg0) {
+            if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != INVISIBLE) {
+                view.setVisibility(INVISIBLE);
+            } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
+                    && view.getVisibility() != VISIBLE) {
+                view.setVisibility(VISIBLE);
+            }
+        }
+
+    }
+
     @Override
     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
         mIsSwitchingState = true;