Fixing state transition bugs

- explicitly keeping track of state in Workspace
- cancelling animations before starting new ones
- adding additional state variable for workspace for in-progress transitions
- updating Scroller object if we jump to a certain location

Change-Id: I5ddf51bae543ec89b2a44ab651d7269eb4859a6d
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index f5a5935..f81a9bf 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -175,6 +175,8 @@
 
     /** The different states that Launcher can be in. */
     private enum State { WORKSPACE, ALL_APPS, CUSTOMIZE, OVERVIEW };
+    private State mState = State.WORKSPACE;
+    private AnimatorSet mStateAnimation;
 
     static final int APPWIDGET_HOST_ID = 1024;
 
@@ -930,21 +932,21 @@
 
     @SuppressWarnings({"UnusedDeclaration"})
     public void previousScreen(View v) {
-        if (!isAllAppsVisible()) {
+        if (mState != State.ALL_APPS) {
             mWorkspace.scrollLeft();
         }
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
     public void nextScreen(View v) {
-        if (!isAllAppsVisible()) {
+        if (mState != State.ALL_APPS) {
             mWorkspace.scrollRight();
         }
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
     public void launchHotSeat(View v) {
-        if (isAllAppsVisible()) return;
+        if (mState == State.ALL_APPS) return;
 
         int index = -1;
         if (v.getId() == R.id.hotseat_left) {
@@ -1172,8 +1174,6 @@
 
             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-            boolean allAppsVisible = isAllAppsVisible();
-            boolean customizationDrawerVisible = isCustomizationDrawerVisible();
 
             // in all these cases, only animate if we're already on home
             if (LauncherApplication.isScreenXLarge()) {
@@ -1181,11 +1181,10 @@
             }
             if (!mWorkspace.isDefaultPageShowing()) {
                 // on the phone, we don't animate the change to the workspace if all apps is visible
-                mWorkspace.moveToDefaultScreen(
-                        alreadyOnHome && (LauncherApplication.isScreenXLarge() || !allAppsVisible));
+                mWorkspace.moveToDefaultScreen(alreadyOnHome &&
+                        (LauncherApplication.isScreenXLarge() || mState != State.ALL_APPS));
             }
-            closeAllApps(alreadyOnHome && allAppsVisible);
-            hideCustomizationDrawer(alreadyOnHome);
+            showWorkspace(alreadyOnHome);
 
             final View v = getWindow().peekDecorView();
             if (v != null && v.getWindowToken() != null) {
@@ -1227,7 +1226,7 @@
         }
 
         // TODO should not do this if the drawer is currently closing.
-        if (isAllAppsVisible()) {
+        if (mState == State.ALL_APPS) {
             outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
         }
 
@@ -1289,7 +1288,7 @@
     public void startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, boolean globalSearch) {
 
-        closeAllApps(true);
+        showWorkspace(true);
 
         if (initialQuery == null) {
             // Use any text typed in the launcher as the initial query
@@ -1407,11 +1406,11 @@
     private void addItems() {
         if (LauncherApplication.isScreenXLarge()) {
             // Animate the widget chooser up from the bottom of the screen
-            if (!isCustomizationDrawerVisible()) {
+            if (mState != State.CUSTOMIZE) {
                 showCustomizationDrawer(true);
             }
         } else {
-            closeAllApps(true);
+            showWorkspace(true);
             showAddDialog(-1, -1);
         }
     }
@@ -1614,7 +1613,7 @@
     }
 
     private void startWallpaper() {
-        closeAllApps(true);
+        showWorkspace(true);
         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
         Intent chooser = Intent.createChooser(pickWallpaper,
                 getText(R.string.chooser_wallpaper));
@@ -1667,10 +1666,8 @@
 
     @Override
     public void onBackPressed() {
-        if (isAllAppsVisible()) {
-            closeAllApps(true);
-        } else if (isCustomizationDrawerVisible()) {
-            hideCustomizationDrawer(true);
+        if (mState == State.ALL_APPS || mState == State.CUSTOMIZE) {
+            showWorkspace(true);
         } else {
             closeFolder();
         }
@@ -1736,8 +1733,8 @@
         } else if (tag instanceof FolderInfo) {
             handleFolderClick((FolderInfo) tag);
         } else if (v == mHandleView) {
-            if (isAllAppsVisible()) {
-                closeAllApps(true);
+            if (mState == State.ALL_APPS) {
+                showWorkspace(true);
             } else {
                 showAllApps(true);
             }
@@ -1746,8 +1743,8 @@
 
     public boolean onTouch(View v, MotionEvent event) {
         // this is an intercepted event being forwarded from mWorkspace;
-        // clicking anywhere on the workspace causes the drawer to slide down
-        hideCustomizationDrawer(true);
+        // clicking anywhere on the workspace causes the customization drawer to slide down
+        showWorkspace(true);
         return false;
     }
 
@@ -1893,21 +1890,21 @@
     public boolean onLongClick(View v) {
         switch (v.getId()) {
             case R.id.previous_screen:
-                if (!isAllAppsVisible()) {
+                if (mState != State.ALL_APPS) {
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                     showPreviews(v);
                 }
                 return true;
             case R.id.next_screen:
-                if (!isAllAppsVisible()) {
+                if (mState != State.ALL_APPS) {
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                     showPreviews(v);
                 }
                 return true;
             case R.id.all_apps_button:
-                if (!isAllAppsVisible()) {
+                if (mState != State.ALL_APPS) {
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                     showPreviews(v);
@@ -2239,7 +2236,7 @@
 
     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
     public boolean isAllAppsVisible() {
-        return mAllAppsGrid != null && mAllAppsGrid.isVisible();
+        return mState == State.ALL_APPS;
     }
 
     // AllAppsView.Watcher
@@ -2401,14 +2398,15 @@
             // toView should appear right at the end of the workspace shrink animation
             final int startDelay = res.getInteger(R.integer.config_workspaceShrinkTime) - duration;
 
-            AnimatorSet s = new AnimatorSet();
-            s.playTogether(scaleAnim, toolbarHideAnim);
-            s.play(scaleAnim).after(startDelay);
+            if (mStateAnimation != null) mStateAnimation.cancel();
+            mStateAnimation = new AnimatorSet();
+            mStateAnimation.playTogether(scaleAnim, toolbarHideAnim);
+            mStateAnimation.play(scaleAnim).after(startDelay);
 
             // Show the new toolbar buttons just as the main animation is ending
             final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
-            s.play(toolbarShowAnim).after(duration + startDelay - fadeInTime);
-            s.start();
+            mStateAnimation.play(toolbarShowAnim).after(duration + startDelay - fadeInTime);
+            mStateAnimation.start();
         } else {
             toView.setTranslationX(0.0f);
             toView.setTranslationY(0.0f);
@@ -2440,12 +2438,13 @@
         mWorkspace.unshrink(animated);
 
         if (animated) {
-            AnimatorSet s = new AnimatorSet();
+            if (mStateAnimation != null) mStateAnimation.cancel();
+            mStateAnimation = new AnimatorSet();
             ValueAnimator scaleAnim = new ObjectAnimator(duration, fromView,
                     new PropertyValuesHolder<Float>("scaleX", scaleFactor),
                     new PropertyValuesHolder<Float>("scaleY", scaleFactor));
             scaleAnim.setInterpolator(new AccelerateInterpolator());
-            s.addListener(new AnimatorListenerAdapter() {
+            mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 public void onAnimationEnd(Animator animation) {
                     fromView.setVisibility(View.GONE);
                     fromView.setScaleX(1.0f);
@@ -2457,13 +2456,13 @@
             AnimatorSet toolbarShowAnim = new AnimatorSet();
             hideAndShowToolbarButtons(State.WORKSPACE, toolbarShowAnim, toolbarHideAnim);
 
-            s.playTogether(scaleAnim, toolbarHideAnim);
+            mStateAnimation.playTogether(scaleAnim, toolbarHideAnim);
 
             // Show the new toolbar buttons at the very end of the whole animation
             final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
             final int unshrinkTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
-            s.play(toolbarShowAnim).after(unshrinkTime - fadeInTime);
-            s.start();
+            mStateAnimation.play(toolbarShowAnim).after(unshrinkTime - fadeInTime);
+            mStateAnimation.start();
         } else {
             fromView.setVisibility(View.GONE);
             hideAndShowToolbarButtons(State.WORKSPACE, null, null);
@@ -2502,8 +2501,9 @@
         }
 
         if (animated) {
-            AnimatorSet s = new AnimatorSet();
-            s.addListener(new AnimatorListenerAdapter() {
+            if (mStateAnimation != null) mStateAnimation.cancel();
+            mStateAnimation = new AnimatorSet();
+            mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 public void onAnimationStart(Animator animation) {
                     toView.setVisibility(View.VISIBLE);
                     toView.setY(toViewStartY);
@@ -2517,15 +2517,15 @@
             AnimatorSet toolbarShowAnim = new AnimatorSet();
             hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
 
-            s.playTogether(
+            mStateAnimation.playTogether(
                     toolbarHideAnim,
                     new ObjectAnimator(duration, fromView, "y", fromViewStartY, fromViewEndY),
                     new ObjectAnimator(duration, toView, "y", toViewStartY, toViewEndY));
 
             // Show the new toolbar buttons just as the main animation is ending
             final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
-            s.play(toolbarShowAnim).after(duration - fadeInTime);
-            s.start();
+            mStateAnimation.play(toolbarShowAnim).after(duration - fadeInTime);
+            mStateAnimation.start();
         } else {
             fromView.setY(fromViewEndY);
             fromView.setVisibility(View.GONE);
@@ -2536,11 +2536,11 @@
     }
 
     void showAllApps(boolean animated) {
-        if (mAllAppsGrid.isVisible())
+        if (mState == State.ALL_APPS)
             return;
 
         if (LauncherApplication.isScreenXLarge()) {
-            if (isCustomizationDrawerVisible()) {
+            if (mState == State.CUSTOMIZE) {
                 cameraPan(State.CUSTOMIZE, State.ALL_APPS, animated);
             } else {
                 cameraZoomOut(State.ALL_APPS, animated);
@@ -2554,6 +2554,28 @@
 
         // TODO: fade these two too
         mDeleteZone.setVisibility(View.GONE);
+        // Change the state *after* we've called all the transition code
+        mState = State.ALL_APPS;
+    }
+
+
+    void showWorkspace(boolean animated) {
+        showWorkspace(animated, null);
+    }
+
+    void showWorkspace(boolean animated, CellLayout layout) {
+        if (layout != null && animated) {
+            mWorkspace.unshrink(layout);
+        } else {
+            mWorkspace.unshrink(animated);
+        }
+        if (mState == State.ALL_APPS) {
+            closeAllApps(animated);
+        } else if (mState == State.CUSTOMIZE) {
+            hideCustomizationDrawer(animated);
+        }
+        // Change the state *after* we've called all the transition code
+        mState = State.WORKSPACE;
     }
 
     /**
@@ -2596,7 +2618,7 @@
      *          - From another workspace
      */
     void closeAllApps(boolean animated) {
-        if (mAllAppsGrid.isVisible()) {
+        if (mState == State.ALL_APPS) {
             mWorkspace.setVisibility(View.VISIBLE);
             if (LauncherApplication.isScreenXLarge()) {
                 cameraZoomIn(State.ALL_APPS, animated);
@@ -2616,23 +2638,20 @@
         // TODO
     }
 
-    private boolean isCustomizationDrawerVisible() {
-        return mHomeCustomizationDrawer != null &&
-                mHomeCustomizationDrawer.getVisibility() == View.VISIBLE;
-    }
-
     // Show the customization drawer (only exists in x-large configuration)
     private void showCustomizationDrawer(boolean animated) {
-        if (isAllAppsVisible()) {
+        if (mState == State.ALL_APPS) {
             cameraPan(State.ALL_APPS, State.CUSTOMIZE, animated);
         } else {
             cameraZoomOut(State.CUSTOMIZE, animated);
         }
+        // Change the state *after* we've called all the transition code
+        mState = State.CUSTOMIZE;
     }
 
     // Hide the customization drawer (only exists in x-large configuration)
     void hideCustomizationDrawer(boolean animated) {
-        if (isCustomizationDrawerVisible()) {
+        if (mState == State.CUSTOMIZE) {
             cameraZoomIn(State.CUSTOMIZE, animated);
         }
     }
@@ -2645,12 +2664,7 @@
 
         if (itemInfo == null) {
             // No items are chosen in All Apps or Customize, so just zoom into the workspace
-            mWorkspace.unshrink(layout);
-            if (isAllAppsVisible()) {
-                closeAllApps(true);
-            } else if (isCustomizationDrawerVisible()) {
-                hideCustomizationDrawer(true);
-            }
+            showWorkspace(true, layout);
         } else {
             // Act as if the chosen item was dropped on the given CellLayout
             if (mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
@@ -2834,8 +2848,7 @@
                 if (mPaused || "lock".equals(reason)) {
                     animate = false;
                 }
-                closeAllApps(animate);
-                hideCustomizationDrawer(animate);
+                showWorkspace(animate);
             }
         }
     }
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index bf0eb12..578bbcc 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -242,7 +242,9 @@
         if (getChildCount() == 0) return;
 
         mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
-        scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0);
+        int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
+        scrollTo(newX, 0);
+        mScroller.setFinalX(newX);
 
         invalidate();
         notifyPageSwitchListener();
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index d9bff5e..881fb59 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -123,8 +123,8 @@
 
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
-    private boolean mIsSmall;
-
+    private boolean mIsSmall = false;
+    private boolean mIsInUnshrinkAnimation = false;
     private AnimatorListener mUnshrinkAnimationListener;
 
     private boolean mInScrollArea = false;
@@ -180,9 +180,11 @@
         mIconCache = app.getIconCache();
 
         mUnshrinkAnimationListener = new AnimatorListener() {
-            public void onAnimationStart(Animator animation) {}
+            public void onAnimationStart(Animator animation) {
+                mIsInUnshrinkAnimation = true;
+            }
             public void onAnimationEnd(Animator animation) {
-                mIsSmall = false;
+                mIsInUnshrinkAnimation = false;
             }
             public void onAnimationCancel(Animator animation) {}
             public void onAnimationRepeat(Animator animation) {}
@@ -362,7 +364,7 @@
 
     public boolean onTouch(View v, MotionEvent event) {
         // this is an intercepted event being forwarded from a cell layout
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             mLauncher.onWorkspaceClick((CellLayout) v);
             return true;
         }
@@ -371,7 +373,7 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             // when the home screens are shrunken, shouldn't allow side-scrolling
             return false;
         }
@@ -380,7 +382,7 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             // when the home screens are shrunken, shouldn't allow side-scrolling
             return false;
         }
@@ -563,7 +565,7 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             // Draw all the workspaces if we're small
             final int pageCount = getChildCount();
             final long drawingTime = getDrawingTime();
@@ -789,10 +791,8 @@
                 CellLayout cl = (CellLayout) getChildAt(i);
                 cl.setX(cl.getX() + delta);
             }
-            snapToPage(newCurrentPage);
-            unshrink();
-
             setCurrentPage(newCurrentPage);
+            unshrink();
         }
     }
 
@@ -802,6 +802,7 @@
 
     void unshrink(boolean animated) {
         if (mIsSmall) {
+            mIsSmall = false;
             AnimatorSet s = new AnimatorSet();
             final int screenCount = getChildCount();
 
@@ -879,7 +880,7 @@
         CellLayout cellLayout;
         int originX = x - xOffset;
         int originY = y - yOffset;
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             cellLayout = findMatchingPageForDragOver(dragView, originX, originY);
             if (cellLayout == null) {
                 // cancel the drag if we're not over a mini-screen at time of drop
@@ -941,7 +942,7 @@
     public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
             DragView dragView, Object dragInfo) {
 
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             // If we're shrunken, don't let anyone drag on folders/etc  that are on the mini-screens
             return null;
         }
@@ -1063,7 +1064,7 @@
         CellLayout currentLayout;
         int originX = x - xOffset;
         int originY = y - yOffset;
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             currentLayout = findMatchingPageForDragOver(dragView, originX, originY);
 
             if (currentLayout == null) {
@@ -1257,7 +1258,7 @@
     public boolean acceptDrop(DragSource source, int x, int y,
             int xOffset, int yOffset, DragView dragView, Object dragInfo) {
         CellLayout layout;
-        if (mIsSmall) {
+        if (mIsSmall || mIsInUnshrinkAnimation) {
             layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset);
             if (layout == null) {
                 // cancel the drag if we're not over a mini-screen at time of drop
@@ -1341,14 +1342,14 @@
 
     @Override
     public void scrollLeft() {
-        if (!mIsSmall) {
+        if (!mIsSmall && !mIsInUnshrinkAnimation) {
             super.scrollLeft();
         }
     }
 
     @Override
     public void scrollRight() {
-        if (!mIsSmall) {
+        if (!mIsSmall && !mIsInUnshrinkAnimation) {
             super.scrollRight();
         }
     }
@@ -1559,12 +1560,10 @@
     }
 
     void moveToDefaultScreen(boolean animate) {
-        if (animate) {
-            if (mIsSmall) {
-                unshrink(mDefaultPage);
-            } else {
-                snapToPage(mDefaultPage);
-            }
+        if (mIsSmall || mIsInUnshrinkAnimation) {
+            mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage));
+        } else if (animate) {
+            snapToPage(mDefaultPage);
         } else {
             setCurrentPage(mDefaultPage);
         }