Changed Workspace to inherit from PagedView

- adds support for seeing dimmed side-pages on
home
- adds workspace support for pages that are
smaller than workspace itself
- removing a lot of code duplicated between
PagedView and Workspace
- tested new Workspace on Nexus One, and perf.
was indistinguishable from old Workspace in my
tests

Change-Id: Ie33ab4e8b35ac01b22894cff66650bb0ece8dfcc
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 4c08ca6..7ae26bb 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -630,10 +630,12 @@
         }
         mDimmedBitmapAlpha = alpha;
         setChildrenAlpha(1.0f - mDimmedBitmapAlpha);
+        invalidate();
     }
 
     private void setChildrenAlpha(float alpha) {
-        for (int i = 0; i < getChildCount(); i++) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
             getChildAt(i).setAlpha(alpha);
         }
     }
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 4d7c666..018b284 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -148,7 +148,7 @@
 
     void onClose() {
         final Workspace workspace = mLauncher.getWorkspace();
-        workspace.getChildAt(workspace.getCurrentScreen()).requestFocus();
+        workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
     }
 
     void bind(FolderInfo info) {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 87e25dc..ae77ec8 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -790,7 +790,7 @@
 
         final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
         if (currentScreen > -1) {
-            mWorkspace.setCurrentScreen(currentScreen);
+            mWorkspace.setCurrentPage(currentScreen);
         }
 
         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
@@ -963,7 +963,7 @@
      */
     View createShortcut(ShortcutInfo info) {
         return createShortcut(R.layout.application,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
     }
 
     /**
@@ -995,7 +995,7 @@
      * @param cellInfo The position on screen where to create the shortcut.
      */
     void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
@@ -1018,7 +1018,7 @@
      * @param cellInfo The position on screen where to create the shortcut.
      */
     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
@@ -1144,7 +1144,7 @@
                 } else {
                     mWorkspace.unshrink(alreadyOnHome);
                 }
-            } else if (!mWorkspace.isDefaultScreenShowing()) {
+            } else if (!mWorkspace.isDefaultPageShowing()) {
                 // on the phone, we don't animate the change to the workspace if all apps is visible
                 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
             }
@@ -1175,7 +1175,7 @@
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
-        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
+        outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPage());
 
         final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
         if (folders.size() > 0) {
@@ -1459,18 +1459,18 @@
         folderInfo.title = getText(R.string.folder_name);
 
         CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         // Update the model
         LauncherModel.addItemToDatabase(this, folderInfo,
                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
+                mWorkspace.getCurrentPage(), cellInfo.cellX, cellInfo.cellY, false);
         mFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo);
         mWorkspace.addInCurrentScreen(newFolder,
                 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
     }
@@ -1480,14 +1480,14 @@
     }
 
     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
-        cellInfo.screen = mWorkspace.getCurrentScreen();
+        cellInfo.screen = mWorkspace.getCurrentPage();
         if (!findSingleSlot(cellInfo)) return;
 
         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
 
         if (!mRestoring) {
             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
-                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
+                (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
                     isWorkspaceLocked());
         }
@@ -1788,10 +1788,10 @@
             Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
             int folderScreen;
             if (openFolder != null) {
-                folderScreen = mWorkspace.getScreenForView(openFolder);
+                folderScreen = mWorkspace.getPageForView(openFolder);
                 // .. and close it
                 closeFolder(openFolder);
-                if (folderScreen != mWorkspace.getCurrentScreen()) {
+                if (folderScreen != mWorkspace.getCurrentPage()) {
                     // Close any folder open on the current screen
                     closeFolder();
                     // Pull the folder onto this screen
@@ -1972,7 +1972,7 @@
             image.setOnClickListener(handler);
             image.setOnFocusChangeListener(handler);
             image.setFocusable(true);
-            if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
+            if (i == mWorkspace.getCurrentPage()) image.requestFocus();
 
             preview.addView(image,
                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
@@ -2009,7 +2009,7 @@
         }
 
         public void onClick(View v) {
-            mWorkspace.snapToScreen((Integer) v.getTag());
+            mWorkspace.snapToPage((Integer) v.getTag());
             v.post(this);
         }
 
@@ -2019,7 +2019,7 @@
 
         public void onFocusChange(View v, boolean hasFocus) {
             if (hasFocus) {
-                mWorkspace.snapToScreen((Integer) v.getTag());
+                mWorkspace.snapToPage((Integer) v.getTag());
             }
         }
     }
@@ -2528,7 +2528,7 @@
                 mAllAppsGrid.zoom(0.0f, animated);
             }
             ((View)mAllAppsGrid).setFocusable(false);
-            mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
         }
     }
 
@@ -2752,7 +2752,7 @@
      */
     public int getCurrentWorkspaceScreen() {
         if (mWorkspace != null) {
-            return mWorkspace.getCurrentScreen();
+            return mWorkspace.getCurrentPage();
         } else {
             return SCREEN_COUNT / 2;
         }
@@ -2805,7 +2805,7 @@
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
                     final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (UserFolderInfo) item);
                     workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             false);
@@ -2813,7 +2813,7 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
                     final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
                             R.layout.live_folder_icon, this,
-                            (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (LiveFolderInfo) item);
                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
                             false);
@@ -2876,7 +2876,7 @@
     public void finishBindingItems() {
         if (mSavedState != null) {
             if (!mWorkspace.hasFocus()) {
-                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+                mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
             }
 
             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 6154947..5052a59 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -42,37 +42,42 @@
 
 /**
  * An abstraction of the original Workspace which supports browsing through a
- * sequential list of "pages" (or PagedViewCellLayouts).
+ * sequential list of "pages"
  */
 public abstract class PagedView extends ViewGroup {
     private static final String TAG = "PagedView";
-    private static final int INVALID_PAGE = -1;
-
-    // the velocity at which a fling gesture will cause us to snap to the next page
-    private static final int SNAP_VELOCITY = 500;
+    protected static final int INVALID_PAGE = -1;
 
     // the min drag distance for a fling to register, to prevent random page shifts
     private static final int MIN_LENGTH_FOR_FLING = 50;
 
-    private boolean mFirstLayout = true;
+    protected static final float NANOTIME_DIV = 1000000000.0f;
 
-    private int mCurrentPage;
-    private int mNextPage = INVALID_PAGE;
-    private Scroller mScroller;
+    // the velocity at which a fling gesture will cause us to snap to the next page
+    protected int mSnapVelocity = 500;
+
+    protected float mSmoothingTime;
+    protected float mTouchX;
+
+    protected boolean mFirstLayout = true;
+
+    protected int mCurrentPage;
+    protected int mNextPage = INVALID_PAGE;
+    protected Scroller mScroller;
     private VelocityTracker mVelocityTracker;
 
     private float mDownMotionX;
     private float mLastMotionX;
     private float mLastMotionY;
 
-    private final static int TOUCH_STATE_REST = 0;
-    private final static int TOUCH_STATE_SCROLLING = 1;
-    private final static int TOUCH_STATE_PREV_PAGE = 2;
-    private final static int TOUCH_STATE_NEXT_PAGE = 3;
+    protected final static int TOUCH_STATE_REST = 0;
+    protected final static int TOUCH_STATE_SCROLLING = 1;
+    protected final static int TOUCH_STATE_PREV_PAGE = 2;
+    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
 
-    private int mTouchState = TOUCH_STATE_REST;
+    protected int mTouchState = TOUCH_STATE_REST;
 
-    private OnLongClickListener mLongClickListener;
+    protected OnLongClickListener mLongClickListener;
 
     private boolean mAllowLongPress = true;
 
@@ -84,13 +89,29 @@
 
     private int mActivePointerId = INVALID_POINTER;
 
+    private enum PageMovingState { PAGE_BEGIN_MOVING, PAGE_END_MOVING };
     private PageSwitchListener mPageSwitchListener;
+    private PageMovingListener mPageMovingListener;
 
     private ArrayList<Boolean> mDirtyPageContent;
     private boolean mDirtyPageAlpha;
 
     protected PagedViewIconCache mPageViewIconCache;
 
+    // If true, syncPages and syncPageItems will be called to refresh pages
+    protected boolean mContentIsRefreshable = true;
+
+    // If true, modify alpha of neighboring pages as user scrolls left/right
+    protected boolean mFadeInAdjacentScreens = true;
+
+    // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
+    // to switch to a new page
+    protected boolean mUsePagingTouchSlop = true;
+
+    // If true, the subclass should directly update mScrollX itself in its computeScroll method
+    // (SmoothPagedView does this)
+    protected boolean mDeferScrollUpdate = false;
+
     /**
      * Simple cache mechanism for PagedViewIcon outlines.
      */
@@ -117,6 +138,11 @@
         void onPageSwitch(View newPage, int newPageIndex);
     }
 
+    public interface PageMovingListener {
+        void onPageBeginMoving();
+        void onPageEndMoving();
+    }
+
     public PagedView(Context context) {
         this(context, null);
     }
@@ -129,13 +155,13 @@
         super(context, attrs, defStyle);
 
         setHapticFeedbackEnabled(false);
-        initWorkspace();
+        init();
     }
 
     /**
      * Initializes various states for this workspace.
      */
-    private void initWorkspace() {
+    protected void init() {
         mDirtyPageContent = new ArrayList<Boolean>();
         mDirtyPageContent.ensureCapacity(32);
         mPageViewIconCache = new PagedViewIconCache();
@@ -190,12 +216,20 @@
         notifyPageSwitchListener();
     }
 
-    private void notifyPageSwitchListener() {
+    protected void notifyPageSwitchListener() {
         if (mPageSwitchListener != null) {
             mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
         }
     }
 
+    // a method that subclasses can override to add behavior
+    protected void pageBeginMoving() {
+    }
+
+    // a method that subclasses can override to add behavior
+    protected void pageEndMoving() {
+    }
+
     /**
      * Registers the specified listener on each page contained in this workspace.
      *
@@ -211,15 +245,31 @@
     }
 
     @Override
-    public void computeScroll() {
+    public void scrollTo(int x, int y) {
+        super.scrollTo(x, y);
+        mTouchX = x;
+        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+    }
+
+    // we moved this functionality to a helper function so SmoothPagedView can reuse it
+    protected boolean computeScrollHelper() {
         if (mScroller.computeScrollOffset()) {
             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
-            postInvalidate();
+            invalidate();
+            return true;
         } else if (mNextPage != INVALID_PAGE) {
             mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
-            notifyPageSwitchListener();
             mNextPage = INVALID_PAGE;
+            notifyPageSwitchListener();
+            pageEndMoving();
+            return true;
         }
+        return false;
+    }
+
+    @Override
+    public void computeScroll() {
+        computeScrollHelper();
     }
 
     @Override
@@ -247,6 +297,7 @@
         if (mFirstLayout) {
             setHorizontalScrollBarEnabled(false);
             scrollTo(mCurrentPage * widthSize, 0);
+            mScroller.setFinalX(mCurrentPage * widthSize);
             setHorizontalScrollBarEnabled(true);
             mFirstLayout = false;
         }
@@ -272,32 +323,67 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
-            int screenCenter = mScrollX + (getMeasuredWidth() / 2);
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; ++i) {
-                PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
-                int childWidth = layout.getMeasuredWidth();
-                int halfChildWidth = (childWidth / 2);
-                int childCenter = getChildOffset(i) + halfChildWidth;
-                int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
-                float alpha = 0.0f;
-                if (distanceFromScreenCenter < halfChildWidth) {
-                    alpha = 1.0f;
-                } else if (distanceFromScreenCenter > childWidth) {
-                    alpha = 0.0f;
-                } else {
-                    float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth;
-                    dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
-                    alpha = 1.0f - dimAlpha;
+        if (mFadeInAdjacentScreens) {
+            if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
+                int screenCenter = mScrollX + (getMeasuredWidth() / 2);
+                final int childCount = getChildCount();
+                for (int i = 0; i < childCount; ++i) {
+                    View layout = (View) getChildAt(i);
+                    int childWidth = layout.getMeasuredWidth();
+                    int halfChildWidth = (childWidth / 2);
+                    int childCenter = getChildOffset(i) + halfChildWidth;
+                    int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+                    float alpha = 0.0f;
+                    if (distanceFromScreenCenter < halfChildWidth) {
+                        alpha = 1.0f;
+                    } else if (distanceFromScreenCenter > childWidth) {
+                        alpha = 0.0f;
+                    } else {
+                        float dimAlpha = (float) (distanceFromScreenCenter - halfChildWidth) / halfChildWidth;
+                        dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
+                        alpha = 1.0f - dimAlpha;
+                    }
+                    if (Float.compare(alpha, layout.getAlpha()) != 0) {
+                        layout.setAlpha(alpha);
+                    }
                 }
-                if (Float.compare(alpha, layout.getAlpha()) != 0) {
-                    layout.setAlpha(alpha);
-                }
+                mDirtyPageAlpha = false;
             }
-            mDirtyPageAlpha = false;
         }
-        super.dispatchDraw(canvas);
+
+        // Find out which screens are visible; as an optimization we only call draw on them
+
+        // As an optimization, this code assumes that all pages have the same width as the 0th
+        // page.
+        final int pageWidth = getChildAt(0).getMeasuredWidth();
+        final int pageCount = getChildCount();
+        final int screenWidth = getMeasuredWidth();
+        int x = getRelativeChildOffset(0) + pageWidth;
+        int leftScreen = 0;
+        int rightScreen = 0;
+        while (x <= mScrollX) {
+            leftScreen++;
+            x += pageWidth;
+            // replace above line with this if you don't assume all pages have same width as 0th
+            // page:
+            // x += getChildAt(leftScreen).getMeasuredWidth();
+        }
+        rightScreen = leftScreen;
+        while (x < mScrollX + screenWidth) {
+            rightScreen++;
+            x += pageWidth;
+            // replace above line with this if you don't assume all pages have same width as 0th
+            // page:
+            //if (rightScreen < pageCount) {
+            //    x += getChildAt(rightScreen).getMeasuredWidth();
+            //}
+        }
+        rightScreen = Math.min(getChildCount() - 1, rightScreen);
+
+        final long drawingTime = getDrawingTime();
+        for (int i = leftScreen; i <= rightScreen; i++) {
+            drawChild(canvas, getChildAt(i), drawingTime);
+        }
     }
 
     @Override
@@ -466,6 +552,7 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 // Release the drag
+                pageEndMoving();
                 mTouchState = TOUCH_STATE_REST;
                 mAllowLongPress = false;
                 mActivePointerId = INVALID_POINTER;
@@ -522,10 +609,13 @@
         boolean yMoved = yDiff > touchSlop;
 
         if (xMoved || yMoved) {
-            if (xPaged) {
+            if (mUsePagingTouchSlop ? xPaged : xMoved) {
                 // Scroll if the user moved far enough along the X axis
                 mTouchState = TOUCH_STATE_SCROLLING;
                 mLastMotionX = x;
+                mTouchX = mScrollX;
+                mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                pageBeginMoving();
             }
             // Either way, cancel any pending longpress
             if (mAllowLongPress) {
@@ -561,6 +651,9 @@
             // Remember where the motion event started
             mDownMotionX = mLastMotionX = ev.getX();
             mActivePointerId = ev.getPointerId(0);
+            if (mTouchState == TOUCH_STATE_SCROLLING) {
+                pageBeginMoving();
+            }
             break;
 
         case MotionEvent.ACTION_MOVE:
@@ -574,14 +667,28 @@
                 int sx = getScrollX();
                 if (deltaX < 0) {
                     if (sx > 0) {
-                        scrollBy(Math.max(-sx, deltaX), 0);
+                        mTouchX += Math.max(-mTouchX, deltaX);
+                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                        if (!mDeferScrollUpdate) {
+                            scrollBy(Math.max(-sx, deltaX), 0);
+                        } else {
+                            // This will trigger a call to computeScroll() on next drawChild() call
+                            invalidate();
+                        }
                     }
                 } else if (deltaX > 0) {
                     final int lastChildIndex = getChildCount() - 1;
                     final int availableToScroll = getChildOffset(lastChildIndex) -
                         getRelativeChildOffset(lastChildIndex) - sx;
                     if (availableToScroll > 0) {
-                        scrollBy(Math.min(availableToScroll, deltaX), 0);
+                        mTouchX += Math.min(availableToScroll, deltaX);
+                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+                        if (!mDeferScrollUpdate) {
+                            scrollBy(Math.min(availableToScroll, deltaX), 0);
+                        } else {
+                            // This will trigger a call to computeScroll() on next drawChild() call
+                            invalidate();
+                        }
                     }
                 } else {
                     awakenScrollBars();
@@ -602,11 +709,12 @@
                 int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
                 boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING;
 
-                if (isfling && velocityX > SNAP_VELOCITY && mCurrentPage > 0) {
-                    snapToPage(mCurrentPage - 1);
-                } else if (isfling && velocityX < -SNAP_VELOCITY &&
+                final int snapVelocity = mSnapVelocity;
+                if (isfling && velocityX > snapVelocity && mCurrentPage > 0) {
+                    snapToPageWithVelocity(mCurrentPage - 1, velocityX);
+                } else if (isfling && velocityX < -snapVelocity &&
                         mCurrentPage < getChildCount() - 1) {
-                    snapToPage(mCurrentPage + 1);
+                    snapToPageWithVelocity(mCurrentPage + 1, velocityX);
                 } else {
                     snapToDestination();
                 }
@@ -701,7 +809,7 @@
         int screenCenter = mScrollX + (getMeasuredWidth() / 2);
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; ++i) {
-            PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
+            View layout = (View) getChildAt(i);
             int childWidth = layout.getMeasuredWidth();
             int halfChildWidth = (childWidth / 2);
             int childCenter = getChildOffset(i) + halfChildWidth;
@@ -714,29 +822,47 @@
         snapToPage(minDistanceFromScreenCenterIndex, 1000);
     }
 
-    void snapToPage(int whichPage) {
+    protected void snapToPageWithVelocity(int whichPage, int velocity) {
+        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
+        // can use it
+        snapToPage(whichPage);
+    }
+
+    protected void snapToPage(int whichPage) {
         snapToPage(whichPage, 1000);
     }
 
-    void snapToPage(int whichPage, int duration) {
+    protected void snapToPage(int whichPage, int duration) {
         whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
 
-        mNextPage = whichPage;
 
         int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
         final int sX = getScrollX();
         final int delta = newX - sX;
+        snapToPage(whichPage, delta, duration);
+    }
+
+    protected void snapToPage(int whichPage, int delta, int duration) {
+        mNextPage = whichPage;
+
+        View focusedChild = getFocusedChild();
+        if (focusedChild != null && whichPage != mCurrentPage &&
+                focusedChild == getChildAt(mCurrentPage)) {
+            focusedChild.clearFocus();
+        }
+
+        pageBeginMoving();
         awakenScrollBars(duration);
         if (duration == 0) {
             duration = Math.abs(delta);
         }
 
         if (!mScroller.isFinished()) mScroller.abortAnimation();
-        mScroller.startScroll(sX, 0, delta, 0, duration);
+        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
 
         // only load some associated pages
         loadAssociatedPages(mNextPage);
-
+        notifyPageSwitchListener();
         invalidate();
     }
 
@@ -793,6 +919,14 @@
         return mAllowLongPress;
     }
 
+    /**
+     * Set true to allow long-press events to be triggered, usually checked by
+     * {@link Launcher} to accept or block dpad-initiated long-presses.
+     */
+    public void setAllowLongPress(boolean allowLongPress) {
+        mAllowLongPress = allowLongPress;
+    }
+
     public static class SavedState extends BaseSavedState {
         int currentPage = -1;
 
@@ -824,23 +958,25 @@
     }
 
     public void loadAssociatedPages(int page) {
-        final int count = getChildCount();
-        if (page < count) {
-            int lowerPageBound = Math.max(0, page - 1);
-            int upperPageBound = Math.min(page + 1, count - 1);
-            for (int i = 0; i < count; ++i) {
-                final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
-                final int childCount = layout.getChildCount();
-                if (lowerPageBound <= i && i <= upperPageBound) {
-                    if (mDirtyPageContent.get(i)) {
-                        syncPageItems(i);
-                        mDirtyPageContent.set(i, false);
+        if (mContentIsRefreshable) {
+            final int count = getChildCount();
+            if (page < count) {
+                int lowerPageBound = Math.max(0, page - 1);
+                int upperPageBound = Math.min(page + 1, count - 1);
+                for (int i = 0; i < count; ++i) {
+                    final ViewGroup layout = (ViewGroup) getChildAt(i);
+                    final int childCount = layout.getChildCount();
+                    if (lowerPageBound <= i && i <= upperPageBound) {
+                        if (mDirtyPageContent.get(i)) {
+                            syncPageItems(i);
+                            mDirtyPageContent.set(i, false);
+                        }
+                    } else {
+                        if (childCount > 0) {
+                            layout.removeAllViews();
+                        }
+                        mDirtyPageContent.set(i, true);
                     }
-                } else {
-                    if (childCount > 0) {
-                        layout.removeAllViews();
-                    }
-                    mDirtyPageContent.set(i, true);
                 }
             }
         }
@@ -861,19 +997,21 @@
     public abstract void syncPageItems(int page);
 
     public void invalidatePageData() {
-        // Update all the pages
-        syncPages();
+        if (mContentIsRefreshable) {
+            // Update all the pages
+            syncPages();
 
-        // Mark each of the pages as dirty
-        final int count = getChildCount();
-        mDirtyPageContent.clear();
-        for (int i = 0; i < count; ++i) {
-            mDirtyPageContent.add(true);
+            // Mark each of the pages as dirty
+            final int count = getChildCount();
+            mDirtyPageContent.clear();
+            for (int i = 0; i < count; ++i) {
+                mDirtyPageContent.add(true);
+            }
+
+            // Load any pages that are necessary for the current window of views
+            loadAssociatedPages(mCurrentPage);
+            mDirtyPageAlpha = true;
+            requestLayout();
         }
-
-        // Load any pages that are necessary for the current window of views
-        loadAssociatedPages(mCurrentPage);
-        mDirtyPageAlpha = true;
-        requestLayout();
     }
 }
diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java
new file mode 100644
index 0000000..3218349
--- /dev/null
+++ b/src/com/android/launcher2/SmoothPagedView.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+
+public abstract class SmoothPagedView extends PagedView {
+    private static final float SMOOTHING_SPEED = 0.75f;
+    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
+
+
+    private static final float BASELINE_FLING_VELOCITY = 2500.f;
+    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
+
+    private WorkspaceOvershootInterpolator mScrollInterpolator;
+
+    private static class WorkspaceOvershootInterpolator implements Interpolator {
+        private static final float DEFAULT_TENSION = 1.3f;
+        private float mTension;
+
+        public WorkspaceOvershootInterpolator() {
+            mTension = DEFAULT_TENSION;
+        }
+
+        public void setDistance(int distance) {
+            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
+        }
+
+        public void disableSettle() {
+            mTension = 0.f;
+        }
+
+        public float getInterpolation(float t) {
+            // _o(t) = t * t * ((tension + 1) * t + tension)
+            // o(t) = _o(t - 1) + 1
+            t -= 1.0f;
+            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
+        }
+    }
+
+    /**
+     * Used to inflate the Workspace from XML.
+     *
+     * @param context The application's context.
+     * @param attrs The attributes set containing the Workspace's customization values.
+     */
+    public SmoothPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * Used to inflate the Workspace from XML.
+     *
+     * @param context The application's context.
+     * @param attrs The attributes set containing the Workspace's customization values.
+     * @param defStyle Unused.
+     */
+    public SmoothPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mUsePagingTouchSlop = false;
+
+        // This means that we'll take care of updating the scroll parameter ourselves (we do it
+        // in computeScroll)
+        mDeferScrollUpdate = true;
+    }
+
+    /**
+     * Initializes various states for this workspace.
+     */
+    @Override
+    protected void init() {
+        super.init();
+        mScrollInterpolator = new WorkspaceOvershootInterpolator();
+        // overwrite the previous mScroller
+        mScroller = new Scroller(getContext(), mScrollInterpolator);
+    }
+
+    @Override
+    protected void snapToDestination() {
+        snapToPageWithVelocity(mCurrentPage, 0);
+    }
+
+    @Override
+    protected void snapToPageWithVelocity(int whichPage, int velocity) {
+        snapToPageWithVelocity(whichPage, 0, true);
+    }
+
+    void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) {
+            // if (!mScroller.isFinished()) return;
+
+        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+
+        final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage));
+        final int newX = whichPage * getWidth();
+        final int delta = newX - mScrollX;
+        int duration = (screenDelta + 1) * 100;
+
+        if (!mScroller.isFinished()) {
+            mScroller.abortAnimation();
+        }
+
+        if (settle) {
+            mScrollInterpolator.setDistance(screenDelta);
+        } else {
+            mScrollInterpolator.disableSettle();
+        }
+
+        velocity = Math.abs(velocity);
+        if (velocity > 0) {
+            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
+                    * FLING_VELOCITY_INFLUENCE;
+        } else {
+            duration += 100;
+        }
+        snapToPage(whichPage, delta, duration);
+    }
+
+    @Override
+    protected void snapToPage(int whichPage) {
+        snapToPageWithVelocity(whichPage, 0, false);
+    }
+
+    @Override
+    public void computeScroll() {
+        boolean scrollComputed = computeScrollHelper();
+
+        if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) {
+            final float now = System.nanoTime() / NANOTIME_DIV;
+            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
+            final float dx = mTouchX - mScrollX;
+            mScrollX += dx * e;
+            mSmoothingTime = now;
+
+            // Keep generating points as long as we're more than 1px away from the target
+            if (dx > 1.f || dx < -1.f) {
+                invalidate();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index ba45b48..7551699 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -19,10 +19,10 @@
 import com.android.launcher.R;
 
 import android.animation.Animatable;
-import android.animation.Animatable.AnimatableListener;
 import android.animation.PropertyAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.Sequencer;
+import android.animation.Animatable.AnimatableListener;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -35,23 +35,16 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.IBinder;
-import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-import android.widget.Scroller;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -59,40 +52,26 @@
 import java.util.HashSet;
 
 /**
- * The workspace is a wide area with a wallpaper and a finite number of screens.
- * Each screen contains a number of icons, folders or widgets the user can
+ * The workspace is a wide area with a wallpaper and a finite number of pages.
+ * Each page contains a number of icons, folders or widgets the user can
  * interact with. A workspace is meant to be used with a fixed width only.
  */
-public class Workspace extends ViewGroup
+public class Workspace extends SmoothPagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
     @SuppressWarnings({"UnusedDeclaration"})
     private static final String TAG = "Launcher.Workspace";
-    private static final int INVALID_SCREEN = -1;
+
     // This is how much the workspace shrinks when we enter all apps or
     // customization mode
     private static final float SHRINK_FACTOR = 0.16f;
-    private static final int SHRINK_TO_TOP = 0;
-    private static final int SHRINK_TO_MIDDLE = 1;
-    private static final int SHRINK_TO_BOTTOM = 2;
-
-    /**
-     * The velocity at which a fling gesture will cause us to snap to the next
-     * screen
-     */
-    private static final int SNAP_VELOCITY = 600;
+    private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
 
     private final WallpaperManager mWallpaperManager;
 
-    private int mDefaultScreen;
+    private int mDefaultPage;
 
-    private boolean mFirstLayout = true;
     private boolean mWaitingToShrinkToBottom = false;
 
-    private int mCurrentScreen;
-    private int mNextScreen = INVALID_SCREEN;
-    private Scroller mScroller;
-    private VelocityTracker mVelocityTracker;
-
     /**
      * CellInfo for the cell that is currently being dragged
      */
@@ -108,16 +87,6 @@
      */
     private CellLayout mDragTargetLayout = null;
 
-    private float mLastMotionX;
-    private float mLastMotionY;
-
-    private final static int TOUCH_STATE_REST = 0;
-    private final static int TOUCH_STATE_SCROLLING = 1;
-
-    private int mTouchState = TOUCH_STATE_REST;
-
-    private OnLongClickListener mLongClickListener;
-
     private Launcher mLauncher;
     private IconCache mIconCache;
     private DragController mDragController;
@@ -128,67 +97,23 @@
     private float[] mTempDragCoordinates = new float[2];
     private float[] mTempDragBottomRightCoordinates = new float[2];
 
-    private boolean mAllowLongPress = true;
-
-    private int mTouchSlop;
-    private int mMaximumVelocity;
-
-    private static final int INVALID_POINTER = -1;
     private static final int DEFAULT_CELL_COUNT_X = 4;
     private static final int DEFAULT_CELL_COUNT_Y = 4;
 
-    private int mActivePointerId = INVALID_POINTER;
-
     private Drawable mPreviousIndicator;
     private Drawable mNextIndicator;
 
-    private static final float NANOTIME_DIV = 1000000000.0f;
-    private static final float SMOOTHING_SPEED = 0.75f;
-    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
-    private float mSmoothingTime;
-    private float mTouchX;
-
-    private WorkspaceOvershootInterpolator mScrollInterpolator;
-
-    private static final float BASELINE_FLING_VELOCITY = 2500.f;
-    private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
-
-    private Paint mDropIndicatorPaint;
-
-    // State variable that indicated whether the screens are small (ie when you're
+    // State variable that indicated whether the pages are small (ie when you're
     // in all apps or customize mode)
     private boolean mIsSmall;
     private AnimatableListener mUnshrinkAnimationListener;
 
-    private static class WorkspaceOvershootInterpolator implements Interpolator {
-        private static final float DEFAULT_TENSION = 1.3f;
-        private float mTension;
-
-        public WorkspaceOvershootInterpolator() {
-            mTension = DEFAULT_TENSION;
-        }
-
-        public void setDistance(int distance) {
-            mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
-        }
-
-        public void disableSettle() {
-            mTension = 0.f;
-        }
-
-        public float getInterpolation(float t) {
-            // _o(t) = t * t * ((tension + 1) * t + tension)
-            // o(t) = _o(t - 1) + 1
-            t -= 1.0f;
-            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
-        }
-    }
 
     /**
      * Used to inflate the Workspace from XML.
      *
      * @param context The application's context.
-     * @param attrs The attribtues set containing the Workspace's customization values.
+     * @param attrs The attributes set containing the Workspace's customization values.
      */
     public Workspace(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -198,11 +123,13 @@
      * Used to inflate the Workspace from XML.
      *
      * @param context The application's context.
-     * @param attrs The attribtues set containing the Workspace's customization values.
+     * @param attrs The attributes set containing the Workspace's customization values.
      * @param defStyle Unused.
      */
     public Workspace(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mContentIsRefreshable = false;
+        mFadeInAdjacentScreens = false;
 
         mWallpaperManager = WallpaperManager.getInstance(context);
 
@@ -210,29 +137,25 @@
                 R.styleable.Workspace, defStyle, 0);
         int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
         int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
-        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
+        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
         a.recycle();
 
         LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
         setHapticFeedbackEnabled(false);
+
         initWorkspace();
     }
 
     /**
      * Initializes various states for this workspace.
      */
-    private void initWorkspace() {
+    protected void initWorkspace() {
         Context context = getContext();
-        mScrollInterpolator = new WorkspaceOvershootInterpolator();
-        mScroller = new Scroller(context, mScrollInterpolator);
-        mCurrentScreen = mDefaultScreen;
-        Launcher.setScreen(mCurrentScreen);
+        mCurrentPage = mDefaultPage;
+        Launcher.setScreen(mCurrentPage);
         LauncherApplication app = (LauncherApplication)context.getApplicationContext();
         mIconCache = app.getIconCache();
 
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        mTouchSlop = configuration.getScaledTouchSlop();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mUnshrinkAnimationListener = new AnimatableListener() {
             public void onAnimationStart(Animatable animation) {}
             public void onAnimationEnd(Animatable animation) {
@@ -241,6 +164,8 @@
             public void onAnimationCancel(Animatable animation) {}
             public void onAnimationRepeat(Animatable animation) {}
         };
+
+        mSnapVelocity = 600;
     }
 
     @Override
@@ -287,10 +212,10 @@
      * @return The open folder on the current screen, or null if there is none
      */
     Folder getOpenFolder() {
-        CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
-        int count = currentScreen.getChildCount();
+        CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
+        int count = currentPage.getChildCount();
         for (int i = 0; i < count; i++) {
-            View child = currentScreen.getChildAt(i);
+            View child = currentPage.getChildAt(i);
             if (child instanceof Folder) {
                 Folder folder = (Folder) child;
                 if (folder.getInfo().opened)
@@ -305,12 +230,10 @@
         ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
 
         for (int screen = 0; screen < screenCount; screen++) {
-            CellLayout currentScreen = (CellLayout) getChildAt(screen);
-            int count = currentScreen.getChildCount();
+            CellLayout currentPage = (CellLayout) getChildAt(screen);
+            int count = currentPage.getChildCount();
             for (int i = 0; i < count; i++) {
-                View child = currentScreen.getChildAt(i);
-                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child
-                        .getLayoutParams();
+                View child = currentPage.getChildAt(i);
                 if (child instanceof Folder) {
                     Folder folder = (Folder) child;
                     if (folder.getInfo().opened)
@@ -323,47 +246,19 @@
         return folders;
     }
 
-    boolean isDefaultScreenShowing() {
-        return mCurrentScreen == mDefaultScreen;
-    }
-
-    /**
-     * Returns the index of the currently displayed screen.
-     *
-     * @return The index of the currently displayed screen.
-     */
-    int getCurrentScreen() {
-        return mCurrentScreen;
+    boolean isDefaultPageShowing() {
+        return mCurrentPage == mDefaultPage;
     }
 
     /**
      * Sets the current screen.
      *
-     * @param currentScreen
+     * @param currentPage
      */
-    void setCurrentScreen(int currentScreen) {
-        setCurrentScreen(currentScreen, true);
-    }
-
-    void setCurrentScreen(int currentScreen, boolean animateScrolling) {
-        setCurrentScreen(currentScreen, animateScrolling, getWidth());
-    }
-
-    void setCurrentScreen(int currentScreen, boolean animateScrolling, int screenWidth) {
-        if (!mScroller.isFinished())
-            mScroller.abortAnimation();
-        mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
-        if (mPreviousIndicator != null) {
-            mPreviousIndicator.setLevel(mCurrentScreen);
-            mNextIndicator.setLevel(mCurrentScreen);
-        }
-        if (animateScrolling) {
-            scrollTo(mCurrentScreen * screenWidth, 0);
-        } else {
-            mScrollX = mCurrentScreen * screenWidth;
-        }
-        updateWallpaperOffset(screenWidth * (getChildCount() - 1));
-        invalidate();
+    @Override
+    void setCurrentPage(int currentPage) {
+        super.setCurrentPage(currentPage);
+        updateWallpaperOffset(mScrollX);
     }
 
     /**
@@ -377,7 +272,7 @@
      * @param spanY The number of cells spanned vertically by the child.
      */
     void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
-        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
+        addInScreen(child, mCurrentPage, x, y, spanX, spanY, false);
     }
 
     /**
@@ -392,7 +287,7 @@
      * @param insert When true, the child is inserted at the beginning of the children list.
      */
     void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
-        addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
+        addInScreen(child, mCurrentPage, x, y, spanX, spanY, insert);
     }
 
     /**
@@ -463,7 +358,7 @@
     }
 
     CellLayout.CellInfo updateOccupiedCellsForCurrentScreen(boolean[] occupied) {
-        CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
+        CellLayout group = (CellLayout) getChildAt(mCurrentPage);
         if (group != null) {
             return group.updateOccupiedCells(occupied, null);
         }
@@ -480,20 +375,38 @@
         return false;
     }
 
-    /**
-     * Registers the specified listener on each screen contained in this workspace.
-     *
-     * @param l The listener used to respond to long clicks.
-     */
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        mLongClickListener = l;
-        final int screenCount = getChildCount();
-        for (int i = 0; i < screenCount; i++) {
-            getChildAt(i).setOnLongClickListener(l);
+    protected void pageBeginMoving() {
+        if (mNextPage != INVALID_PAGE) {
+            // we're snapping to a particular screen
+            enableChildrenCache(mCurrentPage, mNextPage);
+        } else {
+            // this is when user is actively dragging a particular screen, they might
+            // swipe it either left or right (but we won't advance by more than one screen)
+            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
         }
     }
 
+    protected void pageEndMoving() {
+        clearChildrenCache();
+    }
+
+    @Override
+    protected void notifyPageSwitchListener() {
+        super.notifyPageSwitchListener();
+
+        if (mPreviousIndicator != null) {
+            // if we know the next page, we show the indication for it right away; it looks
+            // weird if the indicators are lagging
+            int page = mNextPage;
+            if (page == INVALID_PAGE) {
+                page = mCurrentPage;
+            }
+            mPreviousIndicator.setLevel(page);
+            mNextIndicator.setLevel(page);
+        }
+        Launcher.setScreen(mCurrentPage);
+    };
+
     private void updateWallpaperOffset() {
         updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
     }
@@ -511,86 +424,6 @@
         }
     }
 
-    @Override
-    public void scrollTo(int x, int y) {
-        super.scrollTo(x, y);
-        mTouchX = x;
-        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            mTouchX = mScrollX = mScroller.getCurrX();
-            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-            mScrollY = mScroller.getCurrY();
-            updateWallpaperOffset();
-            postInvalidate();
-        } else if (mNextScreen != INVALID_SCREEN) {
-            mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
-            if (mPreviousIndicator != null) {
-                mPreviousIndicator.setLevel(mCurrentScreen);
-                mNextIndicator.setLevel(mCurrentScreen);
-            }
-            Launcher.setScreen(mCurrentScreen);
-            mNextScreen = INVALID_SCREEN;
-            clearChildrenCache();
-        } else if (mTouchState == TOUCH_STATE_SCROLLING) {
-            final float now = System.nanoTime() / NANOTIME_DIV;
-            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
-            final float dx = mTouchX - mScrollX;
-            mScrollX += dx * e;
-            mSmoothingTime = now;
-
-            // Keep generating points as long as we're more than 1px away from the target
-            if (dx > 1.f || dx < -1.f) {
-                updateWallpaperOffset();
-                postInvalidate();
-            }
-        }
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        boolean restore = false;
-        int restoreCount = 0;
-
-        // ViewGroup.dispatchDraw() supports many features we don't need:
-        // clip to padding, layout animation, animation listener, disappearing
-        // children, etc. The following implementation attempts to fast-track
-        // the drawing dispatch by drawing only what we know needs to be drawn.
-
-        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
-
-        // if the screens are all small, we need to draw all the screens since
-        // they're most likely all visible
-        if (mIsSmall) {
-            final int screenCount = getChildCount();
-            for (int i = 0; i < screenCount; i++) {
-                CellLayout cl = (CellLayout)getChildAt(i);
-                drawChild(canvas, cl, getDrawingTime());
-            }
-        } else if (fastDraw) {
-            // If we are not scrolling or flinging, draw only the current screen
-            drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
-        } else {
-            final long drawingTime = getDrawingTime();
-            final float scrollPos = (float) mScrollX / getWidth();
-            final int leftScreen = (int) scrollPos;
-            final int rightScreen = leftScreen + 1;
-            if (leftScreen >= 0) {
-                drawChild(canvas, getChildAt(leftScreen), drawingTime);
-            }
-            if (scrollPos != leftScreen && rightScreen < getChildCount()) {
-                drawChild(canvas, getChildAt(rightScreen), drawingTime);
-            }
-        }
-
-        if (restore) {
-            canvas.restoreToCount(restoreCount);
-        }
-    }
-
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         computeScroll();
@@ -598,49 +431,8 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        final int width = MeasureSpec.getSize(widthMeasureSpec);
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
-        }
-
-        // The children are given the same width and height as the workspace
-        final int screenCount = getChildCount();
-        for (int i = 0; i < screenCount; i++) {
-            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        if (mFirstLayout) {
-            setHorizontalScrollBarEnabled(false);
-            setCurrentScreen(mCurrentScreen, false, width);
-            setHorizontalScrollBarEnabled(true);
-        }
-    }
-
-    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mFirstLayout) {
-            mFirstLayout = false;
-        }
-        int childLeft = 0;
-        final int screenCount = getChildCount();
-        for (int i = 0; i < screenCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                final int childWidth = child.getMeasuredWidth();
-                child.layout(childLeft, 0,
-                        childLeft + childWidth, child.getMeasuredHeight());
-                childLeft += childWidth;
-            }
-        }
+        super.onLayout(changed, left, top, right, bottom);
 
         // if shrinkToBottom() is called on initialization, it has to be deferred
         // until after the first call to onLayout so that it has the correct width
@@ -652,18 +444,27 @@
         if (LauncherApplication.isInPlaceRotationEnabled()) {
             // When the device is rotated, the scroll position of the current screen
             // needs to be refreshed
-            setCurrentScreen(getCurrentScreen());
+            setCurrentPage(getCurrentPage());
         }
     }
 
     @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
-        int screen = indexOfChild(child);
-        if (screen != mCurrentScreen || !mScroller.isFinished()) {
-            snapToScreen(screen);
-            return true;
+    protected void dispatchDraw(Canvas canvas) {
+        if (mIsSmall) {
+            // Draw all the workspaces if we're small
+            final int pageCount = getChildCount();
+            final long drawingTime = getDrawingTime();
+            for (int i = 0; i < pageCount; i++) {
+                final View page = (View) getChildAt(i);
+
+                if (page.getAlpha() != 1.0f) {
+                    page.setAlpha(1.0f);
+                }
+                drawChild(canvas, page, drawingTime);
+            }
+        } else {
+            super.dispatchDraw(canvas);
         }
-        return false;
     }
 
     @Override
@@ -673,51 +474,20 @@
             if (openFolder != null) {
                 return openFolder.requestFocus(direction, previouslyFocusedRect);
             } else {
-                int focusableScreen;
-                if (mNextScreen != INVALID_SCREEN) {
-                    focusableScreen = mNextScreen;
-                } else {
-                    focusableScreen = mCurrentScreen;
-                }
-                getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
+                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
             }
         }
         return false;
     }
 
     @Override
-    public boolean dispatchUnhandledMove(View focused, int direction) {
-        if (direction == View.FOCUS_LEFT) {
-            if (getCurrentScreen() > 0) {
-                snapToScreen(getCurrentScreen() - 1);
-                return true;
-            }
-        } else if (direction == View.FOCUS_RIGHT) {
-            if (getCurrentScreen() < getChildCount() - 1) {
-                snapToScreen(getCurrentScreen() + 1);
-                return true;
-            }
-        }
-        return super.dispatchUnhandledMove(focused, direction);
-    }
-
-    @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
         if (!mLauncher.isAllAppsVisible()) {
             final Folder openFolder = getOpenFolder();
-            if (openFolder == null) {
-                getChildAt(mCurrentScreen).addFocusables(views, direction);
-                if (direction == View.FOCUS_LEFT) {
-                    if (mCurrentScreen > 0) {
-                        getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
-                    }
-                } else if (direction == View.FOCUS_RIGHT) {
-                    if (mCurrentScreen < getChildCount() - 1) {
-                        getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
-                    }
-                }
-            } else {
+            if (openFolder != null) {
                 openFolder.addFocusables(views, direction);
+            } else {
+                super.addFocusables(views, direction, focusableMode);
             }
         }
     }
@@ -734,213 +504,19 @@
         return super.dispatchTouchEvent(ev);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        if (disallowIntercept) {
-            // We need to make sure to cancel our long press if
-            // a scrollable widget takes over touch events
-            final View currentScreen = getChildAt(mCurrentScreen);
-            currentScreen.cancelLongPress();
-        }
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final boolean allAppsVisible = mLauncher.isAllAppsVisible();
-
-        // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
-        // ie when you click on a mini-screen, it zooms back to that screen)
-        if (!LauncherApplication.isScreenXLarge() && allAppsVisible) {
-            return false; // We don't want the events.  Let them fall through to the all apps view.
-        }
-
-        /*
-         * This method JUST determines whether we want to intercept the motion.
-         * If we return true, onTouchEvent will be called and we do the actual
-         * scrolling there.
-         */
-
-        /*
-         * Shortcut the most recurring case: the user is in the dragging
-         * state and he is moving his finger.  We want to intercept this
-         * motion.
-         */
-        final int action = ev.getAction();
-        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
-            return true;
-        }
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_MOVE: {
-                /*
-                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
-                 * whether the user has moved far enough from his original down touch.
-                 */
-
-                /*
-                 * Locally do absolute value. mLastMotionX is set to the y value
-                 * of the down event.
-                 */
-                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                final float x = ev.getX(pointerIndex);
-                final float y = ev.getY(pointerIndex);
-                final int xDiff = (int) Math.abs(x - mLastMotionX);
-                final int yDiff = (int) Math.abs(y - mLastMotionY);
-
-                final int touchSlop = mTouchSlop;
-                boolean xMoved = xDiff > touchSlop;
-                boolean yMoved = yDiff > touchSlop;
-
-                if (xMoved || yMoved) {
-
-                    if (xMoved) {
-                        // Scroll if the user moved far enough along the X axis
-                        mTouchState = TOUCH_STATE_SCROLLING;
-                        mLastMotionX = x;
-                        mTouchX = mScrollX;
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
-                    }
-                    // Either way, cancel any pending longpress
-                    if (mAllowLongPress) {
-                        mAllowLongPress = false;
-                        // Try canceling the long press. It could also have been scheduled
-                        // by a distant descendant, so use the mAllowLongPress flag to block
-                        // everything
-                        final View currentScreen = getChildAt(mCurrentScreen);
-                        currentScreen.cancelLongPress();
-                    }
-                }
-                break;
-            }
-
-        case MotionEvent.ACTION_DOWN: {
-            final float x = ev.getX();
-            final float y = ev.getY();
-            // Remember location of down touch
-            mLastMotionX = x;
-            mLastMotionY = y;
-            mActivePointerId = ev.getPointerId(0);
-            mAllowLongPress = true;
-
-                /*
-                 * If being flinged and user touches the screen, initiate drag;
-                 * otherwise don't.  mScroller.isFinished should be false when
-                 * being flinged.
-                 */
-                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-
-                if (mTouchState != TOUCH_STATE_SCROLLING) {
-                    final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
-                    if (!currentScreen.lastDownOnOccupiedCell()) {
-                        getLocationOnScreen(mTempCell);
-                        // Send a tap to the wallpaper if the last down was on empty space
-                        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                        mWallpaperManager.sendWallpaperCommand(getWindowToken(),
-                                "android.wallpaper.tap",
-                                mTempCell[0] + (int) ev.getX(pointerIndex),
-                                mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
-                    }
-                }
-
-                // Release the drag
-                clearChildrenCache();
-                mTouchState = TOUCH_STATE_REST;
-                mActivePointerId = INVALID_POINTER;
-                mAllowLongPress = false;
-
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-
-            break;
-
-        case MotionEvent.ACTION_POINTER_UP:
-            onSecondaryPointerUp(ev);
-            break;
-        }
-
-        /*
-         * The only time we want to intercept motion events is if we are in the
-         * drag mode.
-         */
-        return mTouchState != TOUCH_STATE_REST;
-    }
-
-    private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
-                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        final int pointerId = ev.getPointerId(pointerIndex);
-        if (pointerId == mActivePointerId) {
-            // This was our active pointer going up. Choose a new
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionX = ev.getX(newPointerIndex);
-            mLastMotionY = ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
-    /**
-     * If one of our descendant views decides that it could be focused now, only
-     * pass that along if it's on the current screen.
-     *
-     * This happens when live folders requery, and if they're off screen, they
-     * end up calling requestFocus, which pulls it on screen.
-     */
-    @Override
-    public void focusableViewAvailable(View focused) {
-        View current = getChildAt(mCurrentScreen);
-        View v = focused;
-        while (true) {
-            if (v == current) {
-                super.focusableViewAvailable(focused);
-                return;
-            }
-            if (v == this) {
-                return;
-            }
-            ViewParent parent = v.getParent();
-            if (parent instanceof View) {
-                v = (View) v.getParent();
-            } else {
-                return;
-            }
-        }
-    }
-
-    void enableChildrenCache(int fromScreen, int toScreen) {
-        if (fromScreen > toScreen) {
-            final int temp = fromScreen;
-            fromScreen = toScreen;
-            toScreen = temp;
+    void enableChildrenCache(int fromPage, int toPage) {
+        if (fromPage > toPage) {
+            final int temp = fromPage;
+            fromPage = toPage;
+            toPage = temp;
         }
 
         final int screenCount = getChildCount();
 
-        fromScreen = Math.max(fromScreen, 0);
-        toScreen = Math.min(toScreen, screenCount - 1);
+        fromPage = Math.max(fromPage, 0);
+        toPage = Math.min(toPage, screenCount - 1);
 
-        for (int i = fromScreen; i <= toScreen; i++) {
+        for (int i = fromPage; i <= toPage; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
             layout.setChildrenDrawnWithCacheEnabled(true);
             layout.setChildrenDrawingCacheEnabled(true);
@@ -962,105 +538,11 @@
             if (!mScroller.isFinished()) {
                 mScroller.abortAnimation();
             }
-            snapToScreen(mCurrentScreen);
+            snapToPage(mCurrentPage);
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN:
-            /*
-             * If being flinged and user touches, stop the fling. isFinished
-             * will be false if being flinged.
-             */
-            if (!mScroller.isFinished()) {
-                mScroller.abortAnimation();
-            }
-
-            // Remember where the motion event started
-            mLastMotionX = ev.getX();
-            mActivePointerId = ev.getPointerId(0);
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
-            }
-            break;
-        case MotionEvent.ACTION_MOVE:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                // Scroll to follow the motion event
-                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                final float x = ev.getX(pointerIndex);
-                final float deltaX = mLastMotionX - x;
-                mLastMotionX = x;
-
-                if (deltaX < 0) {
-                    if (mTouchX > 0) {
-                        mTouchX += Math.max(-mTouchX, deltaX);
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        invalidate();
-                    }
-                } else if (deltaX > 0) {
-                    final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
-                            mTouchX - getWidth();
-                    if (availableToScroll > 0) {
-                        mTouchX += Math.min(availableToScroll, deltaX);
-                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-                        invalidate();
-                    }
-                } else {
-                    awakenScrollBars();
-                }
-            }
-            break;
-        case MotionEvent.ACTION_UP:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
-                final VelocityTracker velocityTracker = mVelocityTracker;
-                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
-
-                final int screenWidth = getWidth();
-                final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
-                final float scrolledPos = (float) mScrollX / screenWidth;
-
-                if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
-                    // Fling hard enough to move left.
-                    // Don't fling across more than one screen at a time.
-                    final int bound = scrolledPos < whichScreen ?
-                            mCurrentScreen - 1 : mCurrentScreen;
-                    snapToScreen(Math.min(whichScreen, bound), velocityX, true);
-                } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
-                    // Fling hard enough to move right
-                    // Don't fling across more than one screen at a time.
-                    final int bound = scrolledPos > whichScreen ?
-                            mCurrentScreen + 1 : mCurrentScreen;
-                    snapToScreen(Math.max(whichScreen, bound), velocityX, true);
-                } else {
-                    snapToScreen(whichScreen, 0, true);
-                }
-
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
-            }
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            break;
-        case MotionEvent.ACTION_CANCEL:
-            mTouchState = TOUCH_STATE_REST;
-            mActivePointerId = INVALID_POINTER;
-            break;
-        case MotionEvent.ACTION_POINTER_UP:
-            onSecondaryPointerUp(ev);
-            break;
-        }
-
-        return true;
+        return super.onTouchEvent(ev);
     }
 
     public boolean isSmall() {
@@ -1068,11 +550,11 @@
     }
 
     void shrinkToTop(boolean animated) {
-        shrink(SHRINK_TO_TOP, animated);
+        shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
     }
 
     void shrinkToMiddle() {
-        shrink(SHRINK_TO_MIDDLE, true);
+        shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
     }
 
     void shrinkToBottom() {
@@ -1087,35 +569,40 @@
             // to get our width so we can layout the mini-screen views correctly
             mWaitingToShrinkToBottom = true;
         } else {
-            shrink(SHRINK_TO_BOTTOM, animated);
+            shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated);
         }
     }
 
     // we use this to shrink the workspace for the all apps view and the customize view
-    private void shrink(int shrinkPosition, boolean animated) {
+    private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
         mIsSmall = true;
         final Resources res = getResources();
         final int screenWidth = getWidth();
         final int screenHeight = getHeight();
-        final int scaledScreenWidth = (int) (SHRINK_FACTOR * screenWidth);
-        final int scaledScreenHeight = (int) (SHRINK_FACTOR * screenHeight);
+
+        // Making the assumption that all pages have the same width as the 0th
+        final int pageWidth = getChildAt(0).getMeasuredWidth();
+        final int pageHeight = getChildAt(0).getMeasuredHeight();
+
+        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
+        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
         final float scaledSpacing = res.getDimension(R.dimen.smallScreenSpacing);
 
         final int screenCount = getChildCount();
-        float totalWidth = screenCount * scaledScreenWidth + (screenCount - 1) * scaledSpacing;
+        float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * scaledSpacing;
 
         float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
-        if (shrinkPosition == SHRINK_TO_BOTTOM) {
-            newY = screenHeight - newY - scaledScreenHeight;
-        } else if (shrinkPosition == SHRINK_TO_MIDDLE) {
-            newY = screenHeight / 2 - scaledScreenHeight / 2;
+        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) {
+            newY = screenHeight - newY - scaledPageHeight;
+        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
+            newY = screenHeight / 2 - scaledPageHeight / 2;
         }
 
         // We animate all the screens to the centered position in workspace
         // At the same time, the screens become greyed/dimmed
 
         // newX is initialized to the left-most position of the centered screens
-        float newX = (mCurrentScreen + 1) * screenWidth - screenWidth / 2 - totalWidth / 2;
+        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
         for (int i = 0; i < screenCount; i++) {
             CellLayout cl = (CellLayout) getChildAt(i);
             cl.setPivotX(0.0f);
@@ -1136,7 +623,7 @@
                 cl.setDimmedBitmapAlpha(1.0f);
             }
             // increment newX for the next screen
-            newX += scaledScreenWidth + scaledSpacing;
+            newX += scaledPageWidth + scaledSpacing;
             cl.setOnInterceptTouchListener(this);
         }
         setChildrenDrawnWithCacheEnabled(true);
@@ -1144,29 +631,29 @@
 
     // We call this when we trigger an unshrink by clicking on the CellLayout cl
     private void unshrink(CellLayout clThatWasClicked) {
-        int newCurrentScreen = mCurrentScreen;
+        int newCurrentPage = mCurrentPage;
         final int screenCount = getChildCount();
         for (int i = 0; i < screenCount; i++) {
             if (getChildAt(i) == clThatWasClicked) {
-                newCurrentScreen = i;
+                newCurrentPage = i;
             }
         }
-        unshrink(newCurrentScreen);
+        unshrink(newCurrentPage);
     }
 
-    private void unshrink(int newCurrentScreen) {
+    private void unshrink(int newCurrentPage) {
         if (mIsSmall) {
-            int delta = (newCurrentScreen - mCurrentScreen)*getWidth();
+            int delta = (newCurrentPage - mCurrentPage)*getWidth();
 
             final int screenCount = getChildCount();
             for (int i = 0; i < screenCount; i++) {
                 CellLayout cl = (CellLayout) getChildAt(i);
                 cl.setX(cl.getX() + delta);
             }
-            mScrollX = newCurrentScreen * getWidth();
-
+            snapToPage(newCurrentPage);
             unshrink();
-            setCurrentScreen(newCurrentScreen);
+
+            setCurrentPage(newCurrentPage);
         }
     }
 
@@ -1186,11 +673,11 @@
                 cl.setPivotY(0.0f);
                 if (animated) {
                     s.playTogether(
-                            new PropertyAnimator(duration, cl, "translationX", 0.0f),
-                            new PropertyAnimator(duration, cl, "translationY", 0.0f),
-                            new PropertyAnimator(duration, cl, "scaleX", 1.0f),
-                            new PropertyAnimator(duration, cl, "scaleY", 1.0f),
-                            new PropertyAnimator(duration, cl, "dimmedBitmapAlpha", 0.0f));
+                            new PropertyAnimator<Float>(duration, cl, "translationX", 0.0f),
+                            new PropertyAnimator<Float>(duration, cl, "translationY", 0.0f),
+                            new PropertyAnimator<Float>(duration, cl, "scaleX", 1.0f),
+                            new PropertyAnimator<Float>(duration, cl, "scaleY", 1.0f),
+                            new PropertyAnimator<Float>(duration, cl, "dimmedBitmapAlpha", 0.0f));
                 } else {
                     cl.setTranslationX(0.0f);
                     cl.setTranslationY(0.0f);
@@ -1204,58 +691,6 @@
         }
     }
 
-    void snapToScreen(int whichScreen) {
-        snapToScreen(whichScreen, 0, false);
-    }
-
-    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
-        // if (!mScroller.isFinished()) return;
-
-        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
-
-        enableChildrenCache(mCurrentScreen, whichScreen);
-
-        mNextScreen = whichScreen;
-
-        if (mPreviousIndicator != null) {
-            mPreviousIndicator.setLevel(mNextScreen);
-            mNextIndicator.setLevel(mNextScreen);
-        }
-
-        View focusedChild = getFocusedChild();
-        if (focusedChild != null && whichScreen != mCurrentScreen &&
-                focusedChild == getChildAt(mCurrentScreen)) {
-            focusedChild.clearFocus();
-        }
-
-        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
-        final int newX = whichScreen * getWidth();
-        final int delta = newX - mScrollX;
-        int duration = (screenDelta + 1) * 100;
-
-        if (!mScroller.isFinished()) {
-            mScroller.abortAnimation();
-        }
-
-        if (settle) {
-            mScrollInterpolator.setDistance(screenDelta);
-        } else {
-            mScrollInterpolator.disableSettle();
-        }
-
-        velocity = Math.abs(velocity);
-        if (velocity > 0) {
-            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
-                    * FLING_VELOCITY_INFLUENCE;
-        } else {
-            duration += 100;
-        }
-
-        awakenScrollBars(duration);
-        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
-        invalidate();
-    }
-
     void startDrag(CellLayout.CellInfo cellInfo) {
         View child = cellInfo.cell;
 
@@ -1265,32 +700,15 @@
         }
 
         mDragInfo = cellInfo;
-        mDragInfo.screen = mCurrentScreen;
+        mDragInfo.screen = mCurrentPage;
 
-        CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
+        CellLayout current = ((CellLayout) getChildAt(mCurrentPage));
 
         current.onDragChild(child);
         mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
         invalidate();
     }
 
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final SavedState state = new SavedState(super.onSaveInstanceState());
-        state.currentScreen = mCurrentScreen;
-        return state;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        SavedState savedState = (SavedState) state;
-        super.onRestoreInstanceState(savedState.getSuperState());
-        if (savedState.currentScreen != -1) {
-            setCurrentScreen(savedState.currentScreen, false);
-            Launcher.setScreen(mCurrentScreen);
-        }
-    }
-
     void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
         addApplicationShortcut(info, cellInfo, false);
     }
@@ -1319,7 +737,7 @@
             // dragRegionLeft/Top to xOffset and yOffset
             localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
             localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
-            cellLayout =  findMatchingScreenForDragOver(localXY, localBottomRightXY);
+            cellLayout =  findMatchingPageForDragOver(localXY, localBottomRightXY);
             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?
@@ -1335,7 +753,7 @@
             // Move internally
             if (mDragInfo != null) {
                 final View cell = mDragInfo.cell;
-                int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+                int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
                 if (index != mDragInfo.screen) {
                     final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
                     originalCellLayout.removeView(cell);
@@ -1406,7 +824,7 @@
     // 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 findMatchingScreenForDragOver(float[] xy, float[] bottomRightXy) {
+    private CellLayout findMatchingPageForDragOver(float[] xy, float[] bottomRightXy) {
         float x = xy[0];
         float y = xy[1];
         float right = bottomRightXy[0];
@@ -1504,7 +922,7 @@
 
             localBottomRightXY[0] = originX + dragView.getDragRegionWidth();
             localBottomRightXY[1] = originY + dragView.getDragRegionHeight();
-            currentLayout = findMatchingScreenForDragOver(localXY, localBottomRightXY);
+            currentLayout = findMatchingPageForDragOver(localXY, localBottomRightXY);
             if (currentLayout != null) {
                 currentLayout.setHover(true);
             }
@@ -1575,7 +993,7 @@
             break;
         case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
             view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
-                    (ViewGroup) getChildAt(mCurrentScreen),
+                    (ViewGroup) getChildAt(mCurrentPage),
                     ((UserFolderInfo) info));
             break;
         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -1602,7 +1020,7 @@
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
 
             LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen,
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage,
                     lp.cellX, lp.cellY);
         }
     }
@@ -1612,7 +1030,7 @@
      * screen while a scroll is in progress.
      */
     private CellLayout getCurrentDropLayout() {
-        int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
         return (CellLayout) getChildAt(index);
     }
 
@@ -1689,7 +1107,7 @@
      * Estimate the size that a child with the given dimensions will take in the current screen.
      */
     void estimateChildSize(int minWidth, int minHeight, int[] result) {
-        ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result);
+        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
     }
 
     void setLauncher(Launcher launcher) {
@@ -1720,44 +1138,26 @@
         mDragInfo = null;
     }
 
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        super.onRestoreInstanceState(state);
+        Launcher.setScreen(mCurrentPage);
+    }
+
+    @Override
     public void scrollLeft() {
         if (!mIsSmall) {
-            if (mScroller.isFinished()) {
-                if (mCurrentScreen > 0)
-                    snapToScreen(mCurrentScreen - 1);
-            } else {
-                if (mNextScreen > 0)
-                    snapToScreen(mNextScreen - 1);
-            }
+            super.scrollLeft();
         }
     }
 
+    @Override
     public void scrollRight() {
         if (!mIsSmall) {
-            if (mScroller.isFinished()) {
-                if (mCurrentScreen < getChildCount() - 1)
-                    snapToScreen(mCurrentScreen + 1);
-            } else {
-                if (mNextScreen < getChildCount() - 1)
-                    snapToScreen(mNextScreen + 1);
-            }
+            super.scrollRight();
         }
     }
 
-    public int getScreenForView(View v) {
-        int result = -1;
-        if (v != null) {
-            ViewParent vp = v.getParent();
-            final int screenCount = getChildCount();
-            for (int i = 0; i < screenCount; i++) {
-                if (vp == getChildAt(i)) {
-                    return i;
-                }
-            }
-        }
-        return result;
-    }
-
     public Folder getFolderForTag(Object tag) {
         final int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {
@@ -1792,20 +1192,6 @@
         return null;
     }
 
-    /**
-     * @return True is long presses are still allowed for the current touch
-     */
-    public boolean allowLongPress() {
-        return mAllowLongPress;
-    }
-
-    /**
-     * Set true to allow long-press events to be triggered, usually checked by
-     * {@link Launcher} to accept or block dpad-initiated long-presses.
-     */
-    public void setAllowLongPress(boolean allowLongPress) {
-        mAllowLongPress = allowLongPress;
-    }
 
     void removeItems(final ArrayList<ApplicationInfo> apps) {
         final int screenCount = getChildCount();
@@ -1964,50 +1350,29 @@
     void moveToDefaultScreen(boolean animate) {
         if (animate) {
             if (mIsSmall) {
-                unshrink(mDefaultScreen);
+                unshrink(mDefaultPage);
             } else {
-                snapToScreen(mDefaultScreen);
+                snapToPage(mDefaultPage);
             }
         } else {
-            setCurrentScreen(mDefaultScreen);
+            setCurrentPage(mDefaultPage);
         }
-        getChildAt(mDefaultScreen).requestFocus();
+        getChildAt(mDefaultPage).requestFocus();
     }
 
     void setIndicators(Drawable previous, Drawable next) {
         mPreviousIndicator = previous;
         mNextIndicator = next;
-        previous.setLevel(mCurrentScreen);
-        next.setLevel(mCurrentScreen);
+        previous.setLevel(mCurrentPage);
+        next.setLevel(mCurrentPage);
     }
 
-    public static class SavedState extends BaseSavedState {
-        int currentScreen = -1;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        private SavedState(Parcel in) {
-            super(in);
-            currentScreen = in.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(currentScreen);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR =
-                new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
+    @Override
+    public void syncPages() {
     }
+
+    @Override
+    public void syncPageItems(int page) {
+    }
+
 }