Merge "Fixing screen jump when going to spring_loaded mode in rtl" into ub-launcher3-master
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index 4b7b977..c8de9df 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.widget.LinearLayout;
 
 /**
@@ -26,11 +27,13 @@
  */
 public abstract class BaseContainerView extends LinearLayout implements Insettable {
 
+    private final static String TAG = "BaseContainerView";
+
     // The window insets
     private Rect mInsets = new Rect();
     // The bounds of the search bar.  Only the left, top, right are used to inset the
     // search bar and the height is determined by the measurement of the layout
-    private Rect mSearchBarBounds = new Rect();
+    private Rect mFixedSearchBarBounds = new Rect();
     // The bounds of the container
     protected Rect mContentBounds = new Rect();
     // The padding to apply to the container to achieve the bounds
@@ -66,7 +69,11 @@
      * Sets the search bar bounds for this container view to match.
      */
     final public void setSearchBarBounds(Rect bounds) {
-        mSearchBarBounds.set(bounds);
+        if (LauncherAppState.isDogfoodBuild() && !isValidSearchBarBounds(bounds)) {
+            Log.e(TAG, "Invalid search bar bounds: " + bounds);
+        }
+
+        mFixedSearchBarBounds.set(bounds);
 
         // Post the updates since they can trigger a relayout, and this call can be triggered from
         // a layout pass itself.
@@ -83,9 +90,9 @@
      */
     protected void updateBackgroundAndPaddings() {
         Rect padding;
-        Rect searchBarBounds = new Rect(mSearchBarBounds);
-        if (mSearchBarBounds.isEmpty()) {
-            // Use the normal bounds
+        Rect searchBarBounds = new Rect(mFixedSearchBarBounds);
+        if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
+            // Use the default bounds
             padding = new Rect(mInsets.left + mContainerBoundsInset,
                     (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
                     mInsets.right + mContainerBoundsInset,
@@ -99,18 +106,18 @@
         } else {
             // Use the search bounds, if there is a search bar, the bounds will contain
             // the offsets for the insets so we can ignore that
-            padding = new Rect(mSearchBarBounds.left,
+            padding = new Rect(mFixedSearchBarBounds.left,
                     (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
-                    getMeasuredWidth() - mSearchBarBounds.right,
+                    getMeasuredWidth() - mFixedSearchBarBounds.right,
                     mInsets.bottom + mContainerBoundsInset);
         }
-        if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) {
+        if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mFixedSearchBarBounds)) {
             mContentPadding.set(padding);
             mContentBounds.set(padding.left, padding.top,
                     getMeasuredWidth() - padding.right,
                     getMeasuredHeight() - padding.bottom);
-            mSearchBarBounds.set(searchBarBounds);
-            onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
+            mFixedSearchBarBounds.set(searchBarBounds);
+            onUpdateBackgroundAndPaddings(mFixedSearchBarBounds, padding);
         }
     }
 
@@ -118,4 +125,13 @@
      * To be implemented by container views to update themselves when the bounds changes.
      */
     protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
+
+    /**
+     * Returns whether the search bar bounds we got are considered valid.
+     */
+    private boolean isValidSearchBarBounds(Rect searchBarBounds) {
+        return !searchBarBounds.isEmpty() &&
+                searchBarBounds.right <= getMeasuredWidth() &&
+                searchBarBounds.bottom <= getMeasuredHeight();
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9b6c5be..21f23b5 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -959,32 +959,22 @@
         int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
         int centeredLeft = centerX - width / 2;
         int centeredTop = centerY - height / 2;
-        int currentPage = mLauncher.getWorkspace().getNextPage();
-        // In case the workspace is scrolling, we need to use the final scroll to compute
-        // the folders bounds.
-        mLauncher.getWorkspace().setFinalScrollForPageChange(currentPage);
-        // We first fetch the currently visible CellLayoutChildren
-        CellLayout currentLayout = (CellLayout) mLauncher.getWorkspace().getChildAt(currentPage);
-        ShortcutAndWidgetContainer boundingLayout = currentLayout.getShortcutsAndWidgets();
-        Rect bounds = new Rect();
-        parent.getDescendantRectRelativeToSelf(boundingLayout, bounds);
-        // We reset the workspaces scroll
-        mLauncher.getWorkspace().resetFinalScrollForPageChange(currentPage);
 
-        // We need to bound the folder to the currently visible CellLayoutChildren
-        int left = Math.min(Math.max(bounds.left, centeredLeft),
-                bounds.left + bounds.width() - width);
-        int top = Math.min(Math.max(bounds.top, centeredTop),
-                bounds.top + bounds.height() - height);
+        // We need to bound the folder to the currently visible workspace area
+        mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
+        int left = Math.min(Math.max(sTempRect.left, centeredLeft),
+                sTempRect.left + sTempRect.width() - width);
+        int top = Math.min(Math.max(sTempRect.top, centeredTop),
+                sTempRect.top + sTempRect.height() - height);
         if (grid.isPhone && (grid.availableWidthPx - width) < grid.iconSizePx) {
             // Center the folder if it is full (on phones only)
             left = (grid.availableWidthPx - width) / 2;
-        } else if (width >= bounds.width()) {
+        } else if (width >= sTempRect.width()) {
             // If the folder doesn't fit within the bounds, center it about the desired bounds
-            left = bounds.left + (bounds.width() - width) / 2;
+            left = sTempRect.left + (sTempRect.width() - width) / 2;
         }
-        if (height >= bounds.height()) {
-            top = bounds.top + (bounds.height() - height) / 2;
+        if (height >= sTempRect.height()) {
+            top = sTempRect.top + (sTempRect.height() - height) / 2;
         }
 
         int folderPivotX = width / 2 + (centeredLeft - left);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ff4c93a..17fdeb1 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -123,9 +123,10 @@
         allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
         allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
         if (mLauncher != null) {
-            allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
             mLauncher.setAllAppsButton(allAppsButton);
+            allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
             allAppsButton.setOnClickListener(mLauncher);
+            allAppsButton.setOnLongClickListener(mLauncher);
             allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
         }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5768b87..3b10253 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -986,7 +986,7 @@
             // view after launching an app, as they may be depending on the UI to be static to
             // switch to another app, otherwise, if it was
             showAppsView(false /* animated */, false /* resetListToTop */,
-                    !launchedFromApp /* updatePredictedApps */);
+                    !launchedFromApp /* updatePredictedApps */, false /* focusSearchBar */);
         } else if (mOnResumeState == State.WIDGETS) {
             showWidgetsView(false, false);
         }
@@ -2533,12 +2533,17 @@
      */
     protected void onClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
-        if (isAppsViewVisible()) {
-            showWorkspace(true);
-        } else {
-            // Try and refresh the set of predicted apps before we enter launcher
+        if (!isAppsViewVisible()) {
             showAppsView(true /* animated */, false /* resetListToTop */,
-                    true /* updatePredictedApps */);
+                    true /* updatePredictedApps */, false /* focusSearchBar */);
+        }
+    }
+
+    protected void onLongClickAllAppsButton(View v) {
+        if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
+        if (!isAppsViewVisible()) {
+            showAppsView(true /* animated */, false /* resetListToTop */,
+                    true /* updatePredictedApps */, true /* focusSearchBar */);
         }
     }
 
@@ -3115,6 +3120,11 @@
         if (isWorkspaceLocked()) return false;
         if (mState != State.WORKSPACE) return false;
 
+        if (v == mAllAppsButton) {
+            onLongClickAllAppsButton(v);
+            return true;
+        }
+
         if (v instanceof Workspace) {
             if (!mWorkspace.isInOverviewMode()) {
                 if (!mWorkspace.isTouchActive()) {
@@ -3297,14 +3307,15 @@
     /**
      * Shows the apps view.
      */
-    void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps) {
+    void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
+            boolean focusSearchBar) {
         if (resetListToTop) {
             mAppsView.scrollToTop();
         }
         if (updatePredictedApps) {
             tryAndUpdatePredictedApps();
         }
-        showAppsOrWidgets(animated, State.APPS);
+        showAppsOrWidgets(State.APPS, animated, focusSearchBar);
     }
 
     /**
@@ -3315,7 +3326,7 @@
         if (resetPageToZero) {
             mWidgetsView.scrollToTop();
         }
-        showAppsOrWidgets(animated, State.WIDGETS);
+        showAppsOrWidgets(State.WIDGETS, animated, false);
 
         mWidgetsView.post(new Runnable() {
             @Override
@@ -3332,7 +3343,7 @@
      */
     // TODO: calling method should use the return value so that when {@code false} is returned
     // the workspace transition doesn't fall into invalid state.
-    private boolean showAppsOrWidgets(boolean animated, State toState) {
+    private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
         if (mState != State.WORKSPACE &&  mState != State.APPS_SPRING_LOADED &&
                 mState != State.WIDGETS_SPRING_LOADED) {
             return false;
@@ -3342,7 +3353,7 @@
         }
 
         if (toState == State.APPS) {
-            mStateTransitionAnimation.startAnimationToAllApps(mState, animated);
+            mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
         } else {
             mStateTransitionAnimation.startAnimationToWidgets(animated);
         }
@@ -3411,7 +3422,7 @@
     void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
             showAppsView(true /* animated */, false /* resetListToTop */,
-                    false /* updatePredictedApps */);
+                    false /* updatePredictedApps */, false /* focusSearchBar */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
         }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 3d31d33..f63e0f4 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -91,7 +91,6 @@
      */
     static abstract class PrivateTransitionCallbacks {
         void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
-        void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
         float getMaterialRevealViewFinalAlpha(View revealView) {
             return 0;
         }
@@ -108,6 +107,7 @@
                 View allAppsButtonView) {
             return null;
         }
+        void onTransitionComplete() {}
     }
 
     public static final String TAG = "LauncherStateTransitionAnimation";
@@ -128,8 +128,12 @@
 
     /**
      * Starts an animation to the apps view.
+     *
+     * @param startSearchAfterTransition Immediately starts app search after the transition to
+     *                                   All Apps is completed.
      */
-    public void startAnimationToAllApps(final Launcher.State fromState, final boolean animated) {
+    public void startAnimationToAllApps(final boolean animated,
+            final boolean startSearchAfterTransition) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
             private int[] mAllAppsToPanelDelta;
@@ -171,10 +175,17 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                if (startSearchAfterTransition) {
+                    toView.startAppsSearch();
+                }
+            }
         };
         // Only animate the search bar if animating from spring loaded mode back to all apps
         startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
-                toView.getRevealView(), toView.getSearchBarView(), animated, true, cb);
+                toView.getRevealView(), toView.getSearchBarView(), animated,
+                true /* hideSearchBar */, cb);
     }
 
     /**
@@ -345,7 +356,6 @@
 
                     // Hide the reveal view
                     revealView.setVisibility(View.INVISIBLE);
-                    pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
 
                     // Disable all necessary layers
                     for (View v : layerViews.keySet()) {
@@ -360,6 +370,7 @@
 
                     // This can hold unnecessary references to views.
                     mStateAnimation = null;
+                    pCb.onTransitionComplete();
                 }
 
             });
@@ -425,6 +436,7 @@
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
             dispatchOnLauncherTransitionStart(toView, animated, false);
             dispatchOnLauncherTransitionEnd(toView, animated, false);
+            pCb.onTransitionComplete();
         }
     }
 
@@ -678,9 +690,6 @@
                         onCompleteRunnable.run();
                     }
 
-                    // Animation complete callback
-                    pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
-
                     // Disable all necessary layers
                     for (View v : layerViews.keySet()) {
                         if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
@@ -700,6 +709,7 @@
 
                     // This can hold unnecessary references to views.
                     mStateAnimation = null;
+                    pCb.onTransitionComplete();
                 }
             });
 
@@ -735,6 +745,7 @@
             dispatchOnLauncherTransitionPrepare(toView, animated, true);
             dispatchOnLauncherTransitionStart(toView, animated, true);
             dispatchOnLauncherTransitionEnd(toView, animated, true);
+            pCb.onTransitionComplete();
 
             // Run any queued runnables
             if (onCompleteRunnable != null) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 54450f0..c9bbe0a 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -885,11 +885,6 @@
             }
         }
 
-        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
-            updateCurrentPageScroll();
-            mFirstLayout = false;
-        }
-
         final LayoutTransition transition = getLayoutTransition();
         // If the transition is running defer updating max scroll, as some empty pages could
         // still be present, and a max scroll change could cause sudden jumps in scroll.
@@ -914,6 +909,11 @@
             updateMaxScrollX();
         }
 
+        if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
+            updateCurrentPageScroll();
+            mFirstLayout = false;
+        }
+
         if (mScroller.isFinished() && mChildCountOnLastLayout != childCount) {
             if (mRestorePage != INVALID_RESTORE_PAGE) {
                 setCurrentPage(mRestorePage);
@@ -1147,7 +1147,7 @@
             if (!mEdgeGlowRight.isFinished()) {
                 final int restoreCount = canvas.save();
                 Rect display = mViewport;
-                canvas.translate(display.left + mPageScrolls[getChildCount() - 1], display.top);
+                canvas.translate(display.left + mPageScrolls[mIsRtl ? 0 : (getPageCount() - 1)], display.top);
                 canvas.rotate(90);
 
                 getEdgeVerticalPostion(sTmpIntPoint);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index afaecff..6e8e7b3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -241,11 +241,6 @@
     private SparseArray<Parcelable> mSavedStates;
     private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
 
-    // These variables are used for storing the initial and final values during workspace animations
-    private int mSavedScrollX;
-    private float mSavedRotationY;
-    private float mSavedTranslationX;
-
     private float mCurrentScale;
     private float mTransitionProgress;
 
@@ -2770,26 +2765,26 @@
         }
     }
 
-    public void setFinalScrollForPageChange(int pageIndex) {
-        CellLayout cl = (CellLayout) getChildAt(pageIndex);
-        if (cl != null) {
-            mSavedScrollX = getScrollX();
-            mSavedTranslationX = cl.getTranslationX();
-            mSavedRotationY = cl.getRotationY();
-            final int newX = getScrollForPage(pageIndex);
-            setScrollX(newX);
-            cl.setTranslationX(0f);
-            cl.setRotationY(0f);
+    /**
+     * Computes the area relative to dragLayer which is used to display a page.
+     */
+    public void getPageAreaRelativeToDragLayer(Rect outArea) {
+        CellLayout child = (CellLayout) getChildAt(getNextPage());
+        if (child == null) {
+            return;
         }
-    }
+        ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
 
-    public void resetFinalScrollForPageChange(int pageIndex) {
-        if (pageIndex >= 0) {
-            CellLayout cl = (CellLayout) getChildAt(pageIndex);
-            setScrollX(mSavedScrollX);
-            cl.setTranslationX(mSavedTranslationX);
-            cl.setRotationY(mSavedRotationY);
-        }
+        // Use the absolute left instead of the child left, as we want the visible area
+        // irrespective of the visible child. Since the view can only scroll horizontally, the
+        // top position is not affected.
+        mTempXY[0] = getViewportOffsetX() + getPaddingLeft() + boundingLayout.getLeft();
+        mTempXY[1] = child.getTop() + boundingLayout.getTop();
+
+        float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
+        outArea.set(mTempXY[0], mTempXY[1],
+                (int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
+                (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
     }
 
     public void getViewLocationRelativeToSelf(View v, int[] location) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 3b104e2..32c9012 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -284,6 +284,15 @@
         return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView);
     }
 
+    /**
+     * Focuses the search field and begins an app search.
+     */
+    public void startAppsSearch() {
+        if (mSearchBarController != null) {
+            mSearchBarController.focusSearchField();
+        }
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();