diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 7bc36921e2..84a8bce6e6 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -83,7 +83,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { 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 e0e53a6479..2e75579ca8 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -134,15 +134,6 @@ public class LauncherAppState { 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 5fd50818d5..40bd3d43e0 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -71,8 +71,6 @@ import com.android.launcher3.shortcuts.DeepShortcutManager; 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.Iterator; 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 @@ public class LauncherModel extends BroadcastReceiver } @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 @@ public class LauncherModel extends BroadcastReceiver 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 @@ public class LauncherModel extends BroadcastReceiver } } - 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 @@ public class LauncherModel extends BroadcastReceiver 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 @@ public class LauncherModel extends BroadcastReceiver // 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 @@ public class LauncherModel extends BroadcastReceiver 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 @@ public class LauncherModel extends BroadcastReceiver 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 @@ public class LauncherModel extends BroadcastReceiver 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 @@ public class LauncherModel extends BroadcastReceiver } 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; + } + } 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; } - mIsLoaderTaskRunning = false; - mHasLoaderCompletedOnce = true; } } @@ -1605,29 +1604,6 @@ public class LauncherModel extends BroadcastReceiver } } - 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 packagesToIgnore = new HashSet<>(); @@ -1768,11 +1744,8 @@ public class LauncherModel extends BroadcastReceiver } } - 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 @@ public class LauncherModel extends BroadcastReceiver } } } - 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 e71ef2ce52..ad9a13eea0 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -174,7 +174,7 @@ public class LauncherProvider extends ContentProvider { if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) { LauncherAppState app = LauncherAppState.getInstanceNoCreate(); if (app != null) { - app.reloadWorkspace(); + app.getModel().forceReload(); } } } @@ -205,7 +205,7 @@ public class LauncherProvider extends ContentProvider { // 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 4bc40c68f8..cb9227bd97 100644 --- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java +++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java @@ -236,8 +236,7 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { @Override public void run() { ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext); - LauncherAppState.getInstance(mTargetContext).getModel() - .resetLoadedState(true, true); + LauncherAppState.getInstance(mTargetContext).getModel().forceReload(); } }); } catch (Throwable t) {