Merge "Override the overscroll color for the widget rows." into ub-launcher3-burnaby
diff --git a/res/values/config.xml b/res/values/config.xml
index b2ba7a9..93c6d14 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -78,6 +78,9 @@
          get build information. Can be empty. -->
     <string name="build_info_class" translatable="false"></string>
 
+    <!-- View ID to use for QSB widget -->
+    <item type="id" name="qsb_widget" />
+
 <!-- Accessibility actions -->
     <item type="id" name="action_remove" />
     <item type="id" name="action_uninstall" />
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index f76aed7..fcee7e8 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -66,6 +66,7 @@
     private boolean mIsDragging;
     private boolean mIsThumbDetached;
     private boolean mCanThumbDetach;
+    private boolean mIgnoreDragGesture;
 
     // This is the offset from the top of the scrollbar when the user first starts touching.  To
     // prevent jumping, this offset is applied as the user scrolls.
@@ -180,13 +181,15 @@
         int y = (int) ev.getY();
         switch (action) {
             case MotionEvent.ACTION_DOWN:
-                if (isNearPoint(downX, downY)) {
+                if (isNearThumb(downX, downY)) {
                     mTouchOffset = downY - mThumbOffset.y;
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
-                // Check if we should start scrolling
-                if (!mIsDragging && isNearPoint(downX, downY) &&
+                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
+                // exceeded some fixed movement
+                mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop();
+                if (!mIsDragging && !mIgnoreDragGesture && isNearThumb(downX, lastY) &&
                         Math.abs(y - downY) > config.getScaledTouchSlop()) {
                     mRv.getParent().requestDisallowInterceptTouchEvent(true);
                     mIsDragging = true;
@@ -214,6 +217,7 @@
             case MotionEvent.ACTION_CANCEL:
                 mTouchOffset = 0;
                 mLastTouchY = 0;
+                mIgnoreDragGesture = false;
                 if (mIsDragging) {
                     mIsDragging = false;
                     mPopup.animateVisibility(false);
@@ -287,7 +291,7 @@
     /**
      * Returns whether the specified points are near the scroll bar bounds.
      */
-    private boolean isNearPoint(int x, int y) {
+    private boolean isNearThumb(int x, int y) {
         mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
                 mThumbOffset.y + mThumbHeight);
         mTmpRect.inset(mTouchInset, mTouchInset);
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ea1c0fd..59ab839 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -616,7 +616,9 @@
             // Check the DB first.
             if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
                 try {
-                    PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+                    int flags = UserHandleCompat.myUserHandle().equals(user) ? 0 :
+                        PackageManager.GET_UNINSTALLED_PACKAGES;
+                    PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
                     ApplicationInfo appInfo = info.applicationInfo;
                     if (appInfo == null) {
                         throw new NameNotFoundException("ApplicationInfo is null");
@@ -787,7 +789,7 @@
     }
 
     private static final class IconDB extends SQLiteOpenHelper {
-        private final static int DB_VERSION = 6;
+        private final static int DB_VERSION = 7;
 
         private final static String TABLE_NAME = "icons";
         private final static String COLUMN_ROWID = "rowid";
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6f2458c..1f843cb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -301,6 +301,8 @@
     private boolean mHasFocus = false;
     private boolean mAttached = false;
 
+    private LauncherClings mClings;
+
     private static LongArrayMap<FolderInfo> sFolders = new LongArrayMap<>();
 
     private View.OnTouchListener mHapticFeedbackTouchListener;
@@ -648,7 +650,7 @@
     public boolean isDraggingEnabled() {
         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
         // that is subsequently removed from the workspace in startBinding().
-        return !mModel.isLoadingWorkspace();
+        return !isWorkspaceLoading();
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@@ -994,6 +996,12 @@
         mPaused = false;
         if (mRestoring || mOnResumeNeedsLoad) {
             setWorkspaceLoading(true);
+
+            // If we're starting binding all over again, clear any bind calls we'd postponed in
+            // the past (see waitUntilResume) -- we don't need them since we're starting binding
+            // from scratch again
+            mBindOnResumeCallbacks.clear();
+
             mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
             mRestoring = false;
             mOnResumeNeedsLoad = false;
@@ -3499,6 +3507,7 @@
             mAppWidgetHost.setQsbWidgetId(widgetId);
             if (widgetId != -1) {
                 mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
+                mQsb.setId(R.id.qsb_widget);
                 mQsb.updateAppWidgetOptions(opts);
                 mQsb.setPadding(0, 0, 0, 0);
                 mSearchDropTargetBar.addView(mQsb);
@@ -3755,11 +3764,12 @@
                 continue;
             }
 
+            final View view;
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                     ShortcutInfo info = (ShortcutInfo) item;
-                    View shortcut = createShortcut(info);
+                    view = createShortcut(info);
 
                     /*
                      * TODO: FIX collision case
@@ -3778,28 +3788,26 @@
                             }
                         }
                     }
-
-                    workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
-                            item.cellY, 1, 1);
-                    if (animateIcons) {
-                        // Animate all the applications up now
-                        shortcut.setAlpha(0f);
-                        shortcut.setScaleX(0f);
-                        shortcut.setScaleY(0f);
-                        bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
-                        newShortcutsScreenId = item.screenId;
-                    }
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                    FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
+                    view = FolderIcon.fromXml(R.layout.folder_icon, this,
                             (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                             (FolderInfo) item, mIconCache);
-                    workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
-                            item.cellY, 1, 1);
                     break;
                 default:
                     throw new RuntimeException("Invalid Item Type");
             }
+
+            workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
+                    item.cellY, 1, 1);
+            if (animateIcons) {
+                // Animate all the applications up now
+                view.setAlpha(0f);
+                view.setScaleX(0f);
+                view.setScaleY(0f);
+                bounceAnims.add(createNewAppBounceAnimation(view, i));
+                newShortcutsScreenId = item.screenId;
+            }
         }
 
         if (animateIcons) {
@@ -4064,7 +4072,8 @@
 
     private boolean canRunNewAppsAnimation() {
         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
-        return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
+        return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000)
+                && (mClings == null || !mClings.isVisible());
     }
 
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
@@ -4491,6 +4500,7 @@
         // launcher2). Otherwise, we prompt the user upon started for migration
         LauncherClings launcherClings = new LauncherClings(this);
         if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
+            mClings = launcherClings;
             if (mModel.canMigrateFromOldLauncherDb(this)) {
                 launcherClings.showMigrationCling();
             } else {
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 7470284..18fe8ef 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -51,6 +51,7 @@
 
     @Thunk Launcher mLauncher;
     private LayoutInflater mInflater;
+    @Thunk boolean mIsVisible;
 
     /** Ctor */
     public LauncherClings(Launcher launcher) {
@@ -91,6 +92,7 @@
      * package was not preinstalled and there exists a db to migrate from.
      */
     public void showMigrationCling() {
+        mIsVisible = true;
         mLauncher.hideWorkspaceSearchAndHotseat();
 
         ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
@@ -117,6 +119,7 @@
     }
 
     public void showLongPressCling(boolean showWelcome) {
+        mIsVisible = true;
         ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
         View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
 
@@ -196,6 +199,7 @@
                     mLauncher.getSharedPrefs().edit()
                         .putBoolean(flag, true)
                         .apply();
+                    mIsVisible = false;
                     if (postAnimationCb != null) {
                         postAnimationCb.run();
                     }
@@ -209,6 +213,10 @@
         }
     }
 
+    public boolean isVisible() {
+        return mIsVisible;
+    }
+
     /** Returns whether the clings are enabled or should be shown */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private boolean areClingsEnabled() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 2748682..e5ca778 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -34,7 +34,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -259,7 +258,7 @@
 
     /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
      * posted on the worker thread handler. */
-    private static void runOnWorkerThread(Runnable r) {
+    @Thunk static void runOnWorkerThread(Runnable r) {
         if (sWorkerThread.getThreadId() == Process.myTid()) {
             r.run();
         } else {
@@ -268,19 +267,6 @@
         }
     }
 
-    /**
-     * Runs the specified runnable after the loader is complete
-     */
-    @Thunk void runAfterBindCompletes(Runnable r) {
-        if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) {
-            synchronized (mBindCompleteRunnables) {
-                mBindCompleteRunnables.add(r);
-            }
-        } else {
-            runOnWorkerThread(r);
-        }
-    }
-
     boolean canMigrateFromOldLauncherDb(Launcher launcher) {
         return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
     }
@@ -894,8 +880,13 @@
     }
 
     private void assertWorkspaceLoaded() {
-        if (LauncherAppState.isDogfoodBuild() && (isLoadingWorkspace() || !mHasLoaderCompletedOnce)) {
-            throw new RuntimeException("Trying to add shortcut while loader is running");
+        if (LauncherAppState.isDogfoodBuild()) {
+            synchronized (mLock) {
+                if (!mHasLoaderCompletedOnce ||
+                        (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
+                    throw new RuntimeException("Trying to add shortcut while loader is running");
+                }
+            }
         }
     }
 
@@ -1390,16 +1381,6 @@
                 mHandler.post(r);
             }
         }
-
-        // Run all the bind complete runnables after workspace is bound.
-        if (!mBindCompleteRunnables.isEmpty()) {
-            synchronized (mBindCompleteRunnables) {
-                for (final Runnable r : mBindCompleteRunnables) {
-                    runOnWorkerThread(r);
-                }
-                mBindCompleteRunnables.clear();
-            }
-        }
     }
 
     public void stopLoader() {
@@ -1441,15 +1422,6 @@
         return mAllAppsLoaded;
     }
 
-    boolean isLoadingWorkspace() {
-        synchronized (mLock) {
-            if (mLoaderTask != null) {
-                return mLoaderTask.isLoadingWorkspace();
-            }
-        }
-        return false;
-    }
-
     /**
      * Runnable for the thread that loads the contents of the launcher:
      *   - workspace icons
@@ -1468,10 +1440,6 @@
             mFlags = flags;
         }
 
-        boolean isLoadingWorkspace() {
-            return mIsLoadingAndBindingWorkspace;
-        }
-
         private void loadAndBindWorkspace() {
             mIsLoadingAndBindingWorkspace = true;
 
@@ -2697,13 +2665,24 @@
                         callbacks.finishBindingItems();
                     }
 
+                    mIsLoadingAndBindingWorkspace = false;
+
+                    // Run all the bind complete runnables after workspace is bound.
+                    if (!mBindCompleteRunnables.isEmpty()) {
+                        synchronized (mBindCompleteRunnables) {
+                            for (final Runnable r : mBindCompleteRunnables) {
+                                runOnWorkerThread(r);
+                            }
+                            mBindCompleteRunnables.clear();
+                        }
+                    }
+
                     // If we're profiling, ensure this is the last thing in the queue.
                     if (DEBUG_LOADERS) {
                         Log.d(TAG, "bound workspace in "
                             + (SystemClock.uptimeMillis()-t) + "ms");
                     }
 
-                    mIsLoadingAndBindingWorkspace = false;
                 }
             };
             if (isLoadingSynchronously) {
@@ -2832,12 +2811,27 @@
 
                 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
                 if (heuristic != null) {
-                    runAfterBindCompletes(new Runnable() {
+                    final Runnable r = new Runnable() {
 
                         @Override
                         public void run() {
                             heuristic.processUserApps(apps);
                         }
+                    };
+                    runOnMainThread(new Runnable() {
+
+                        @Override
+                        public void run() {
+                            // Check isLoadingWorkspace on the UI thread, as it is updated on
+                            // the UI thread.
+                            if (mIsLoadingAndBindingWorkspace) {
+                                synchronized (mBindCompleteRunnables) {
+                                    mBindCompleteRunnables.add(r);
+                                }
+                            } else {
+                                runOnWorkerThread(r);
+                            }
+                        }
                     });
                 }
             }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index e33b4db..2f66e2c 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -113,6 +113,10 @@
      * Scrolls this recycler view to the top.
      */
     public void scrollToTop() {
+        // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
+        if (mScrollbar.isThumbDetached()) {
+            mScrollbar.reattachThumbToScroll();
+        }
         scrollToPosition(0);
     }