Merge "Adding some tests for request pin shortcut/widget flow" into ub-launcher3-dorval
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 7bc3692..84a8bce 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -83,7 +83,7 @@
 
         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
         if (app != null) {
-            app.reloadWorkspace();
+            app.getModel().forceReload();
         }
         asyncResult.finish();
     }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index e0e53a6..2e75579 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -134,15 +134,6 @@
         PackageInstallerCompat.getInstance(mContext).onStop();
     }
 
-    /**
-     * Reloads the workspace items from the DB and re-binds the workspace. This should generally
-     * not be called as DB updates are automatically followed by UI update
-     */
-    public void reloadWorkspace() {
-        mModel.resetLoadedState(false, true);
-        mModel.startLoaderFromBackground();
-    }
-
     LauncherModel setLauncher(Launcher launcher) {
         getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5fd5081..40bd3d4 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -71,8 +71,6 @@
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -93,6 +91,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 
 /**
@@ -123,12 +122,11 @@
     }
     @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
 
-    // We start off with everything not loaded.  After that, we assume that
+    // Indicates whether the current model data is valid or not.
+    // We start off with everything not loaded. After that, we assume that
     // our monitoring of the package manager provides all updates and we never
-    // need to do a requery.  These are only ever touched from the loader thread.
-    private boolean mWorkspaceLoaded;
-    private boolean mAllAppsLoaded;
-    private boolean mDeepShortcutsLoaded;
+    // need to do a requery. This is only ever touched from the loader thread.
+    private boolean mModelLoaded;
 
     /**
      * Set of runnables to be called on the background thread after the workspace binding
@@ -148,11 +146,11 @@
     private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
         @Override
         public void run() {
-            if (mDeepShortcutsLoaded) {
+            if (mModelLoaded) {
                 boolean hasShortcutHostPermission =
                         DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
                 if (hasShortcutHostPermission != mHasShortcutHostPermission) {
-                    mApp.reloadWorkspace();
+                    forceReload();
                 }
             }
         }
@@ -480,8 +478,16 @@
         }
     }
 
-    void forceReload() {
-        resetLoadedState(true, true);
+    /**
+     * Reloads the workspace items from the DB and re-binds the workspace. This should generally
+     * not be called as DB updates are automatically followed by UI update
+     */
+    public void forceReload() {
+        synchronized (mLock) {
+            // Stop any existing loaders first, so they don't set mModelLoaded to true later
+            stopLoaderLocked();
+            mModelLoaded = false;
+        }
 
         // Do this here because if the launcher activity is running it will be restarted.
         // If it's not running startLoaderFromBackground will merely tell it that it needs
@@ -489,19 +495,6 @@
         startLoaderFromBackground();
     }
 
-    public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
-        synchronized (mLock) {
-            // Stop any existing loaders first, so they don't set mAllAppsLoaded or
-            // mWorkspaceLoaded to true later
-            stopLoaderLocked();
-            if (resetAllAppsLoaded) mAllAppsLoaded = false;
-            if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
-            // Always reset deep shortcuts loaded.
-            // TODO: why?
-            mDeepShortcutsLoaded = false;
-        }
-    }
-
     /**
      * When the launcher is in the background, it's possible for it to miss paired
      * configuration changes.  So whenever we trigger the loader from the background
@@ -553,9 +546,8 @@
                 // If there is already one running, tell it to stop.
                 stopLoaderLocked();
                 mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
-                // TODO: mDeepShortcutsLoaded does not need to be true for synchronous bind.
-                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded
-                        && mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {
+                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
+                        && mModelLoaded && !mIsLoaderTaskRunning) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                     return true;
                 } else {
@@ -607,28 +599,6 @@
             mPageToBindFirst = pageToBindFirst;
         }
 
-        private void loadAndBindWorkspace() {
-            mIsLoadingAndBindingWorkspace = true;
-
-            // Load the workspace
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
-            }
-
-            if (!mWorkspaceLoaded) {
-                loadWorkspace();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mWorkspaceLoaded = true;
-                }
-            }
-
-            // Bind the workspace
-            bindWorkspace(mPageToBindFirst);
-        }
-
         private void waitForIdle() {
             // Wait until the either we're stopped or the other threads are done.
             // This way we don't start loading all apps until the workspace has settled
@@ -671,7 +641,7 @@
                 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
                         "valid page index");
             }
-            if (!mAllAppsLoaded || !mWorkspaceLoaded) {
+            if (!mModelLoaded) {
                 // Ensure that we don't try and bind a specified page when the pages have not been
                 // loaded already (we should load everything asynchronously in that case)
                 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
@@ -703,6 +673,14 @@
             bindDeepShortcuts();
         }
 
+        private void verifyNotStopped() throws CancellationException {
+            synchronized (LoaderTask.this) {
+                if (mStopped) {
+                    throw new CancellationException("Loader stopped");
+                }
+            }
+        }
+
         public void run() {
             synchronized (mLock) {
                 if (mStopped) {
@@ -710,41 +688,62 @@
                 }
                 mIsLoaderTaskRunning = true;
             }
-            // Optimize for end-user experience: if the Launcher is up and // running with the
-            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
-            // workspace first (default).
-            keep_running: {
-                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
-                loadAndBindWorkspace();
 
-                if (mStopped) {
-                    break keep_running;
-                }
+            try {
+                if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+                // Set to false in bindWorkspace()
+                mIsLoadingAndBindingWorkspace = true;
+                loadWorkspace();
 
+                verifyNotStopped();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+                bindWorkspace(mPageToBindFirst);
+
+                // Take a break
+                if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
                 waitForIdle();
+                verifyNotStopped();
 
                 // second step
-                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
-                loadAndBindAllApps();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+                loadAllApps();
 
+                verifyNotStopped();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
+                updateIconCache();
+
+                // Take a break
+                if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
                 waitForIdle();
+                verifyNotStopped();
 
                 // third step
-                if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
-                loadAndBindDeepShortcuts();
-            }
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+                loadDeepShortcuts();
 
-            // Clear out this reference, otherwise we end up holding it until all of the
-            // callback runnables are done.
-            mContext = null;
+                verifyNotStopped();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+                bindDeepShortcuts();
 
-            synchronized (mLock) {
-                // If we are still the last one to be scheduled, remove ourselves.
-                if (mLoaderTask == this) {
-                    mLoaderTask = null;
+                synchronized (mLock) {
+                    // Everything loaded bind the data.
+                    mModelLoaded = true;
+                    mHasLoaderCompletedOnce = true;
                 }
-                mIsLoaderTaskRunning = false;
-                mHasLoaderCompletedOnce = true;
+            } catch (CancellationException e) {
+              // Loader stopped, ignore
+            } finally {
+                // Clear out this reference, otherwise we end up holding it until all of the
+                // callback runnables are done.
+                mContext = null;
+
+                synchronized (mLock) {
+                    // If we are still the last one to be scheduled, remove ourselves.
+                    if (mLoaderTask == this) {
+                        mLoaderTask = null;
+                    }
+                    mIsLoaderTaskRunning = false;
+                }
             }
         }
 
@@ -1605,29 +1604,6 @@
             }
         }
 
-        private void loadAndBindAllApps() {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
-            }
-            if (!mAllAppsLoaded) {
-                loadAllApps();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                }
-                updateIconCache();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mAllAppsLoaded = true;
-                }
-            } else {
-                onlyBindAllApps();
-            }
-        }
-
         private void updateIconCache() {
             // Ignore packages which have a promise icon.
             HashSet<String> packagesToIgnore = new HashSet<>();
@@ -1768,11 +1744,8 @@
             }
         }
 
-        private void loadAndBindDeepShortcuts() {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
-            }
-            if (!mDeepShortcutsLoaded) {
+        private void loadDeepShortcuts() {
+            if (!mModelLoaded) {
                 sBgDataModel.deepShortcutMap.clear();
                 DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
                 mHasShortcutHostPermission = shortcutManager.hasHostPermission();
@@ -1785,14 +1758,7 @@
                         }
                     }
                 }
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mDeepShortcutsLoaded = true;
-                }
             }
-            bindDeepShortcuts();
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e71ef2c..ad9a13e 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -174,7 +174,7 @@
         if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
             LauncherAppState app = LauncherAppState.getInstanceNoCreate();
             if (app != null) {
-                app.reloadWorkspace();
+                app.getModel().forceReload();
             }
         }
     }
@@ -205,7 +205,7 @@
             // Deprecated behavior to support legacy devices which rely on provider callbacks.
             LauncherAppState app = LauncherAppState.getInstanceNoCreate();
             if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
-                app.reloadWorkspace();
+                app.getModel().forceReload();
             }
 
             String notify = uri.getQueryParameter("notify");
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index 1ed4a24..47b43f5 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -266,8 +266,7 @@
                 @Override
                 public void run() {
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
-                    LauncherAppState.getInstance(mTargetContext).getModel()
-                            .resetLoadedState(true, true);
+                    LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
                 }
             });
         } catch (Throwable t) {