Merge "Fixing crash in All Apps." into ub-launcher3-burnaby
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index b247145..f7adaf8 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -269,19 +269,8 @@
 
         // Start the drag
         mLauncher.getWorkspace().beginDragShared(v, mLastTouchPos, this, false);
-
-        // We delay entering spring-loaded mode slightly to make sure the UI
-        // thready is free of any work.
-        postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                // We don't enter spring-loaded mode if the drag has been cancelled
-                if (mLauncher.getDragController().isDragging()) {
-                    // Go into spring loaded mode (must happen before we startDrag())
-                    mLauncher.enterSpringLoadedDragMode();
-                }
-            }
-        }, 150);
+        // Enter spring loaded mode
+        mLauncher.enterSpringLoadedDragMode();
 
         return false;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7688a3d..4533089 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -522,13 +522,19 @@
         mLauncherCallbacks.setLauncherAppsCallback(new Launcher.LauncherAppsCallbacks() {
             @Override
             public void onAllAppsBoundsChanged(Rect bounds) {
+                if (LOGD) {
+                    Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds);
+                }
                 mAppsView.setFixedBounds(Launcher.this, bounds);
             }
 
             @Override
             public void dismissAllApps() {
-                showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true, null,
-                        false /* notifyLauncherCallbacks */);
+                // Dismiss All Apps if we aren't already paused/invisible
+                if (!mPaused) {
+                    showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true,
+                            null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */);
+                }
             }
         });
         return true;
@@ -1012,6 +1018,13 @@
         }
         mOnResumeState = State.NONE;
 
+        // Restore the apps state if we are in all apps
+        if (mState == State.APPS) {
+            if (mLauncherCallbacks != null) {
+                mLauncherCallbacks.onAllAppsShown();
+            }
+        }
+
         // Background was set to gradient in onPause(), restore to black if in all apps.
         setWorkspaceBackground(mState == State.WORKSPACE);
 
@@ -1072,7 +1085,7 @@
                 mWorkspace.getCustomContentCallbacks().onShow(true);
             }
         }
-        mWorkspace.updateInteractionForState();
+        updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
         mWorkspace.onResume();
 
         if (!isWorkspaceLoading()) {
@@ -2096,8 +2109,6 @@
     public void startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, boolean globalSearch) {
 
-        showWorkspace(true);
-
         if (initialQuery == null) {
             // Use any text typed in the launcher as the initial query
             initialQuery = getTypedText();
@@ -2116,6 +2127,9 @@
         if (clearTextImmediately) {
             clearTypedText();
         }
+
+        // We need to show the workspace after starting the search
+        showWorkspace(true);
     }
 
     /**
@@ -2861,6 +2875,21 @@
         }
     }
 
+    /** Updates the interaction state. */
+    public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
+        // Only update the interacting state if we are transitioning to/from a view without an
+        // overlay
+        boolean fromStateWithoutOverlay = fromState != Workspace.State.NORMAL &&
+                fromState != Workspace.State.NORMAL_HIDDEN;
+        boolean toStateWithoutOverlay = toState != Workspace.State.NORMAL &&
+                toState != Workspace.State.NORMAL_HIDDEN;
+        if (toStateWithoutOverlay) {
+            onInteractionBegin();
+        } else if (fromStateWithoutOverlay) {
+            onInteractionEnd();
+        }
+    }
+
     void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
         try {
             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
@@ -3165,7 +3194,6 @@
 
         if (v instanceof Workspace) {
             if (!mWorkspace.isInOverviewMode()) {
-
                 if (!mWorkspace.isTouchActive()) {
                     showOverviewMode(true);
                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
@@ -3337,8 +3365,12 @@
             // Send an accessibility event to announce the context change
             getWindow().getDecorView()
                     .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-
-            onWorkspaceShown(animated);
+            if (notifyLauncherCallbacks) {
+                // Dismiss all apps when the workspace is shown
+                if (mLauncherCallbacks != null) {
+                    mLauncherCallbacks.onAllAppsHidden();
+                }
+            }
         }
     }
 
@@ -3348,10 +3380,6 @@
                 WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
                 null /* onCompleteRunnable */);
         mState = State.WORKSPACE;
-        onWorkspaceShown(animated);
-    }
-
-    public void onWorkspaceShown(boolean animated) {
     }
 
     /**
@@ -3411,6 +3439,18 @@
                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    /**
+     * Updates the workspace and interaction state on state change, and return the animation to this
+     * new state.
+     */
+    public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
+            boolean animated, HashMap<View, Integer> layerViews) {
+        Workspace.State fromState = mWorkspace.getState();
+        Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
+        updateInteraction(fromState, toState);
+        return anim;
+    }
+
     public void enterSpringLoadedDragMode() {
         Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s",
                 mState.name()));
@@ -3429,6 +3469,14 @@
             final Runnable onCompleteRunnable) {
         if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
 
+        if (successfulDrop) {
+            // We need to trigger all apps hidden to notify search to update itself before the
+            // delayed call to showWorkspace below
+            if (mLauncherCallbacks != null) {
+                mLauncherCallbacks.onAllAppsHidden();
+            }
+        }
+
         mHandler.postDelayed(new Runnable() {
             @Override
             public void run() {
@@ -3449,13 +3497,10 @@
 
     void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
-            mStateTransitionAnimation.startAnimationToAllApps(true /* animated */);
-            mState = State.APPS;
+            showAppsView(true, false);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
-            mStateTransitionAnimation.startAnimationToWidgets(true /* animated */);
-            mState = State.WIDGETS;
+            showWidgetsView(true, false);
         }
-        // Otherwise, we are not in spring loaded mode, so don't do anything.
     }
 
     void lockAllApps() {
@@ -3579,14 +3624,13 @@
      * in onResume.
      *
      * This needs to be called from incoming places where resources might have been loaded
-     * while we are paused.  That is becaues the Configuration might be wrong
-     * when we're not running, and if it comes back to what it was when we
-     * were paused, we are not restarted.
+     * while the activity is paused. That is because the Configuration (e.g., rotation)  might be
+     * wrong when we're not running, and if the activity comes back to what the configuration was
+     * when we were paused, activity is not restarted.
      *
      * Implementation of the method from LauncherModel.Callbacks.
      *
-     * @return true if we are currently paused.  The caller might be able to
-     * skip some work in that case since we will come back again.
+     * @return {@code true} if we are currently paused. The caller might be able to skip some work
      */
     private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
         if (mPaused) {
@@ -4130,10 +4174,6 @@
         if (mAppsView != null) {
             mAppsView.setApps(apps);
         }
-        if (mWidgetsView != null) {
-            mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
-                    getPackageManager());
-        }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.bindAllApplications(apps);
         }
@@ -4269,26 +4309,23 @@
         }
     }
 
-    /**
-     * A number of packages were updated.
-     */
     @Thunk ArrayList<Object> mWidgetsAndShortcuts;
     private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
             public void run() {
-                bindPackagesUpdated(mWidgetsAndShortcuts);
-                mWidgetsAndShortcuts = null;
+                bindAllPackages(mWidgetsAndShortcuts);
             }
         };
 
-    public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
+    @Override
+    public void bindAllPackages(final ArrayList<Object> widgetsAndShortcuts) {
         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
             mWidgetsAndShortcuts = widgetsAndShortcuts;
             return;
         }
 
-        if (mWidgetsView != null) {
-            mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
-                    getPackageManager());
+        if (mWidgetsView != null && widgetsAndShortcuts != null) {
+            mWidgetsView.addWidgets(widgetsAndShortcuts, getPackageManager());
+            mWidgetsAndShortcuts = null;
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2fee81c..25c86c9 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -51,6 +51,7 @@
     public void finishBindingItems(final boolean upgradePath);
     public void onClickAllAppsButton(View v);
     public void onAllAppsShown();
+    public void onAllAppsHidden();
     public void bindAllApplications(ArrayList<AppInfo> apps);
     public void onClickFolderIcon(View v);
     public void onClickAppShortcut(View v);
diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java
index e4fdbbc..8174af0 100644
--- a/src/com/android/launcher3/LauncherExtension.java
+++ b/src/com/android/launcher3/LauncherExtension.java
@@ -128,6 +128,10 @@
         }
 
         @Override
+        public void onAllAppsHidden() {
+        }
+
+        @Override
         public void bindAllApplications(ArrayList<AppInfo> apps) {
         }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e62c689..7fdd523 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -197,7 +197,7 @@
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
-        public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
+        public void bindAllPackages(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
@@ -1599,9 +1599,6 @@
                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                 loadAndBindAllApps();
 
-                // Remove entries for packages which changed while the launcher was dead.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews();
-
                 // Restore the default thread priority after we are done loading items
                 synchronized (mLock) {
                     android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
@@ -2870,6 +2867,7 @@
                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(added);
+                        loadAndBindWidgetsAndShortcuts(mContext,callbacks);
                         if (DEBUG_LOADERS) {
                             Log.d(TAG, "bound " + added.size() + " apps in "
                                 + (SystemClock.uptimeMillis() - bindTime) + "ms");
@@ -3285,29 +3283,33 @@
         runOnWorkerThread(new Runnable(){
             @Override
             public void run() {
-                final ArrayList<Object> list =
-                        getSortedWidgetsAndShortcuts(context, true /* refresh */);
+                final ArrayList<Object> list = getWidgetsAndShortcuts(context, true /* refresh */);
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindPackagesUpdated(list);
+                            callbacks.bindAllPackages(list);
                         }
                     }
                 });
+                // update the Widget entries inside DB on the worker thread.
+                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(list);
             }
         });
     }
 
-    // Returns a list of ResolveInfos/AppWidgetInfos in sorted order
-    public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context, boolean refresh) {
+    /**
+     *  Returns a list of ResolveInfos/AppWidgetInfos.
+     *
+     *  @see #loadAndBindWidgetsAndShortcuts
+     */
+    private ArrayList<Object> getWidgetsAndShortcuts(Context context, boolean refresh) {
         PackageManager packageManager = context.getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
         widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
         widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
-        Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
         return widgetsAndShortcuts;
     }
 
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 9e005f2..51f84bf 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -249,8 +249,8 @@
 
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
-        Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation(
-                toWorkspaceState, -1, animated, layerViews);
+        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
+                animated, layerViews);
 
         if (animated && initialized) {
             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
@@ -546,8 +546,8 @@
 
         // Create the workspace animation.
         // NOTE: this call apparently also sets the state for the workspace if !animated
-        Animator workspaceAnim = mLauncher.getWorkspace().setStateWithAnimation(
-                toWorkspaceState, toWorkspacePage, animated, layerViews);
+        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
+                toWorkspacePage, animated, layerViews);
 
         if (animated && initialized) {
             mStateAnimation = LauncherAnimUtils.createAnimatorSet();
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 44a76b7..4cdf1ca 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -180,9 +180,6 @@
         prepareStartAnimation(mDropTargetBar);
         mShowDropTargetBarAnim.start();
         hideSearchBar(true);
-        if (mQSBSearchBar != null) {
-            mQSBSearchBar.setVisibility(View.GONE);
-        }
     }
 
     /**
@@ -193,9 +190,6 @@
         prepareStartAnimation(mDropTargetBar);
         mShowDropTargetBarAnim.reverse();
         showSearchBar(true);
-        if (mQSBSearchBar != null) {
-            mQSBSearchBar.setVisibility(View.VISIBLE);
-        }
     }
 
     /*
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 412cbcd..93bfeaf 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.os.Process;
 import android.util.Log;
 import android.util.LongSparseArray;
 
@@ -34,6 +35,9 @@
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetCell;
 
+import junit.framework.Assert;
+
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -202,11 +206,14 @@
      *   2. Any preview for an absent package is removed
      * This ensures that we remove entries for packages which changed while the launcher was dead.
      */
-    public void removeObsoletePreviews() {
+    public void removeObsoletePreviews(ArrayList<Object> list) {
+        // This method should always be called from the worker thread.
+        Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
+
         LongSparseArray<UserHandleCompat> userIdCache = new LongSparseArray<>();
         LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
 
-        for (Object obj : LauncherModel.getSortedWidgetsAndShortcuts(mContext, false)) {
+        for (Object obj : list) {
             final UserHandleCompat user;
             final String pkg;
             if (obj instanceof ResolveInfo) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4c76092..e7a41e0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2047,14 +2047,6 @@
         return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
     }
 
-    public void updateInteractionForState() {
-        if (mState != State.NORMAL) {
-            mLauncher.onInteractionBegin();
-        } else {
-            mLauncher.onInteractionEnd();
-        }
-    }
-
     /**
      * Sets the current workspace {@link State}, returning an animation transitioning the workspace
      * to that new state.
@@ -2067,7 +2059,6 @@
 
         // Update the current state
         mState = toState;
-        updateInteractionForState();
         updateAccessibilityFlags();
 
         return workspaceAnim;