summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/LauncherModel.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/LauncherModel.java')
-rw-r--r--src/com/android/launcher3/LauncherModel.java223
1 files changed, 149 insertions, 74 deletions
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b60477fa0..b5922c6a3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.app.SearchManager;
+import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -33,7 +34,6 @@ import android.content.pm.ResolveInfo;
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;
@@ -55,6 +55,7 @@ import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.MigrateFromRestoreTask;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.CursorIconInfo;
@@ -203,7 +204,7 @@ public class LauncherModel extends BroadcastReceiver
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
public void bindAllPackages(WidgetsModel model);
- public void bindSearchablesChanged();
+ public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
public void dumpLogsToLocalData();
@@ -257,7 +258,7 @@ public class LauncherModel extends BroadcastReceiver
/** 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 {
@@ -266,19 +267,6 @@ public class LauncherModel extends BroadcastReceiver
}
}
- /**
- * 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() ;
}
@@ -892,8 +880,13 @@ public class LauncherModel extends BroadcastReceiver
}
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");
+ }
+ }
}
}
@@ -1133,7 +1126,7 @@ public class LauncherModel extends BroadcastReceiver
* Update the order of the workspace screens in the database. The array list contains
* a list of screen ids in the order that they should appear.
*/
- void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+ public void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
final ContentResolver cr = context.getContentResolver();
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1280,14 +1273,14 @@ public class LauncherModel extends BroadcastReceiver
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
- } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
- SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
+ } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
Callbacks callbacks = getCallback();
if (callbacks != null) {
- callbacks.bindSearchablesChanged();
+ callbacks.bindSearchProviderChanged();
}
} else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ UserManagerCompat.getInstance(context).enableAndResetCache();
forceReload();
}
}
@@ -1388,16 +1381,6 @@ public class LauncherModel extends BroadcastReceiver
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() {
@@ -1411,7 +1394,7 @@ public class LauncherModel extends BroadcastReceiver
/**
* Loads the workspace screen ids in an ordered list.
*/
- @Thunk static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
+ public static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
final ContentResolver contentResolver = context.getContentResolver();
final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1439,15 +1422,6 @@ public class LauncherModel extends BroadcastReceiver
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
@@ -1466,10 +1440,6 @@ public class LauncherModel extends BroadcastReceiver
mFlags = flags;
}
- boolean isLoadingWorkspace() {
- return mIsLoadingAndBindingWorkspace;
- }
-
private void loadAndBindWorkspace() {
mIsLoadingAndBindingWorkspace = true;
@@ -1641,11 +1611,12 @@ public class LauncherModel extends BroadcastReceiver
}
// check & update map of what's occupied; used to discard overlapping/invalid items
- private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item) {
+ private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
+ ArrayList<Long> workspaceScreens) {
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- final int countX = (int) profile.numColumns;
- final int countY = (int) profile.numRows;
+ final int countX = profile.numColumns;
+ final int countY = profile.numRows;
long containerIndex = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -1687,7 +1658,12 @@ public class LauncherModel extends BroadcastReceiver
occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
return true;
}
- } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (!workspaceScreens.contains((Long) item.screenId)) {
+ // The item has an invalid screen id.
+ return false;
+ }
+ } else {
// Skip further checking if it is not the hotseat or workspace container
return true;
}
@@ -1754,8 +1730,27 @@ public class LauncherModel extends BroadcastReceiver
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- int countX = (int) profile.numColumns;
- int countY = (int) profile.numRows;
+ int countX = profile.numColumns;
+ int countY = profile.numRows;
+
+ if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) {
+ long migrationStartTime = System.currentTimeMillis();
+ Log.v(TAG, "Starting workspace migration after restore");
+ try {
+ MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext);
+ // Clear the flags before starting the task, so that we do not run the task
+ // again, in case there was an uncaught error.
+ MigrateFromRestoreTask.clearFlags(mContext);
+ task.execute();
+ } catch (Exception e) {
+ Log.e(TAG, "Error during grid migration", e);
+
+ // Clear workspace.
+ mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
+ }
+ Log.v(TAG, "Workspace migration completed in "
+ + (System.currentTimeMillis() - migrationStartTime));
+ }
if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
@@ -1776,6 +1771,7 @@ public class LauncherModel extends BroadcastReceiver
clearSBgDataStructures();
final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
.getInstance(mContext).updateAndGetActiveSessionCache();
+ sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final ArrayList<Long> restoredRows = new ArrayList<Long>();
@@ -1977,6 +1973,7 @@ public class LauncherModel extends BroadcastReceiver
} catch (URISyntaxException e) {
Launcher.addDumpLog(TAG,
"Invalid uri: " + intentDescription, true);
+ itemsToRemove.add(id);
continue;
}
@@ -2047,7 +2044,7 @@ public class LauncherModel extends BroadcastReceiver
}
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, info)) {
+ if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2098,7 +2095,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.options = c.getInt(optionsIndex);
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo)) {
+ if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2163,14 +2160,18 @@ public class LauncherModel extends BroadcastReceiver
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
provider.provider);
- int status = restoreStatus;
+ // The provider is available. So the widget is either
+ // available or not available. We do not need to track
+ // any future restore updates.
+ int status = restoreStatus &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
if (!wasProviderReady) {
// If provider was not previously ready, update the
// status and UI flag.
// Id would be valid only if the widget restore broadcast was received.
if (isIdValid) {
- status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
status &= ~LauncherAppWidgetInfo
.FLAG_PROVIDER_NOT_READY;
@@ -2214,13 +2215,14 @@ public class LauncherModel extends BroadcastReceiver
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Log.e(TAG, "Widget found where container != " +
- "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ itemsToRemove.add(id);
continue;
}
appWidgetInfo.container = container;
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo)) {
+ if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2279,6 +2281,21 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ // Sort all the folder items and make sure the first 3 items are high resolution.
+ for (FolderInfo folder : sBgFolders) {
+ Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+ int pos = 0;
+ for (ShortcutInfo info : folder.contents) {
+ if (info.usingLowResIcon) {
+ info.updateIcon(mIconCache, false);
+ }
+ pos ++;
+ if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ }
+
if (restoredRows.size() > 0) {
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
@@ -2294,8 +2311,6 @@ public class LauncherModel extends BroadcastReceiver
null, sWorker);
}
- sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
// Remove any empty screens
ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
for (ItemInfo item: sBgItemsIdMap) {
@@ -2657,13 +2672,24 @@ public class LauncherModel extends BroadcastReceiver
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) {
@@ -2792,12 +2818,27 @@ public class LauncherModel extends BroadcastReceiver
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);
+ }
+ }
});
}
}
@@ -2825,8 +2866,7 @@ public class LauncherModel extends BroadcastReceiver
// Cleanup any data stored for a deleted user.
ManagedProfileHeuristic.processAllUsers(profiles, mContext);
- loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
- true /* refresh */);
+ loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2895,7 +2935,7 @@ public class LauncherModel extends BroadcastReceiver
}
// Reload widget list. No need to refresh, as we only want to update the icons and labels.
- loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false);
+ loadAndBindWidgetsAndShortcuts(callbacks, false);
}
void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -3155,7 +3195,15 @@ public class LauncherModel extends BroadcastReceiver
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
- widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ widgetInfo.restoreStatus &=
+ ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+ // adding this flag ensures that launcher shows 'click to setup'
+ // if the widget has a config activity. In case there is no config
+ // activity, it will be marked as 'restored' during bind.
+ widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
widgets.add(widgetInfo);
updateItemInDatabase(context, widgetInfo);
}
@@ -3235,8 +3283,36 @@ public class LauncherModel extends BroadcastReceiver
});
}
- // onProvidersChanged method (API >= 17) already refreshed the widget list
- loadAndBindWidgetsAndShortcuts(context, callbacks, Build.VERSION.SDK_INT < 17);
+ // Update widgets
+ if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) {
+ // Always refresh for a package event on secondary user
+ boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle());
+
+ // Refresh widget list, if the package already had a widget.
+ synchronized (sBgLock) {
+ if (sBgWidgetProviders != null) {
+ HashSet<String> pkgSet = new HashSet<>();
+ Collections.addAll(pkgSet, mPackages);
+
+ for (ComponentKey key : sBgWidgetProviders.keySet()) {
+ needToRefresh |= key.user.equals(mUser) &&
+ pkgSet.contains(key.componentName.getPackageName());
+ }
+ }
+ }
+
+ if (!needToRefresh && mOp != OP_REMOVE) {
+ // Refresh widget list, if there is any newly added widget
+ PackageManager pm = context.getPackageManager();
+ for (String pkg : mPackages) {
+ needToRefresh |= !pm.queryBroadcastReceivers(
+ new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
+ .setPackage(pkg), 0).isEmpty();
+ }
+ }
+
+ loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
+ }
// Write all the logs to disk
mHandler.post(new Runnable() {
@@ -3310,13 +3386,12 @@ public class LauncherModel extends BroadcastReceiver
}
}
- public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks,
- final boolean refresh) {
+ public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
runOnWorkerThread(new Runnable() {
@Override
public void run() {
- updateWidgetsModel(context, refresh);
+ updateWidgetsModel(refresh);
final WidgetsModel model = mBgWidgetsModel.clone();
mHandler.post(new Runnable() {
@@ -3340,10 +3415,10 @@ public class LauncherModel extends BroadcastReceiver
*
* @see #loadAndBindWidgetsAndShortcuts
*/
- @Thunk void updateWidgetsModel(Context context, boolean refresh) {
- PackageManager packageManager = context.getPackageManager();
+ @Thunk void updateWidgetsModel(boolean refresh) {
+ PackageManager packageManager = mApp.getContext().getPackageManager();
final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
- widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
+ widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);