2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.launcher3;
19 import android.app.SearchManager;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProviderInfo;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.ContentProviderClient;
25 import android.content.ContentProviderOperation;
26 import android.content.ContentResolver;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.Intent.ShortcutIconResource;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.LauncherApps.Callback;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ProviderInfo;
36 import android.content.pm.ResolveInfo;
37 import android.content.res.Configuration;
38 import android.content.res.Resources;
39 import android.database.Cursor;
40 import android.graphics.Bitmap;
41 import android.graphics.BitmapFactory;
42 import android.net.Uri;
43 import android.os.Environment;
44 import android.os.Handler;
45 import android.os.HandlerThread;
46 import android.os.Parcelable;
47 import android.os.Process;
48 import android.os.RemoteException;
49 import android.os.SystemClock;
50 import android.provider.BaseColumns;
51 import android.text.TextUtils;
52 import android.util.Log;
53 import android.util.Pair;
55 import com.android.launcher3.compat.AppWidgetManagerCompat;
56 import com.android.launcher3.compat.LauncherActivityInfoCompat;
57 import com.android.launcher3.compat.LauncherAppsCompat;
58 import com.android.launcher3.compat.PackageInstallerCompat;
59 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
60 import com.android.launcher3.compat.UserHandleCompat;
61 import com.android.launcher3.compat.UserManagerCompat;
63 import java.lang.ref.WeakReference;
64 import java.net.URISyntaxException;
65 import java.security.InvalidParameterException;
66 import java.text.Collator;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collection;
70 import java.util.Collections;
71 import java.util.Comparator;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Map.Entry;
78 import java.util.TreeMap;
81 * Maintains in-memory state of the Launcher. It is expected that there should be only one
82 * LauncherModel object held in a static. Also provide APIs for updating the database state
85 public class LauncherModel extends BroadcastReceiver
86 implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
87 static final boolean DEBUG_LOADERS = false;
88 private static final boolean DEBUG_RECEIVER = false;
89 private static final boolean REMOVE_UNRESTORED_ICONS = true;
90 private static final boolean ADD_MANAGED_PROFILE_SHORTCUTS = false;
92 static final String TAG = "Launcher.Model";
94 // true = use a "More Apps" folder for non-workspace apps on upgrade
95 // false = strew non-workspace apps across the workspace on upgrade
96 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
97 public static final int LOADER_FLAG_NONE = 0;
98 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
99 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
101 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
102 private static final long INVALID_SCREEN_ID = -1L;
104 private final boolean mAppsCanBeOnRemoveableStorage;
105 private final boolean mOldContentProviderExists;
107 private final LauncherAppState mApp;
108 private final Object mLock = new Object();
109 private DeferredHandler mHandler = new DeferredHandler();
110 private LoaderTask mLoaderTask;
111 private boolean mIsLoaderTaskRunning;
112 private volatile boolean mFlushingWorkerThread;
115 * Maintain a set of packages per user, for which we added a shortcut on the workspace.
117 private static final String INSTALLED_SHORTCUTS_SET_PREFIX = "installed_shortcuts_set_for_user_";
119 // Specific runnable types that are run on the main thread deferred handler, this allows us to
120 // clear all queued binding runnables when the Launcher activity is destroyed.
121 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
122 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
124 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
126 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
128 sWorkerThread.start();
130 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
132 // We start off with everything not loaded. After that, we assume that
133 // our monitoring of the package manager provides all updates and we never
134 // need to do a requery. These are only ever touched from the loader thread.
135 private boolean mWorkspaceLoaded;
136 private boolean mAllAppsLoaded;
138 // When we are loading pages synchronously, we can't just post the binding of items on the side
139 // pages as this delays the rotation process. Instead, we wait for a callback from the first
140 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
141 // a normal load, we also clear this set of Runnables.
142 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
144 private WeakReference<Callbacks> mCallbacks;
146 // < only access in worker thread >
147 AllAppsList mBgAllAppsList;
149 // The lock that must be acquired before referencing any static bg data structures. Unlike
150 // other locks, this one can generally be held long-term because we never expect any of these
151 // static data structures to be referenced outside of the worker thread except on the first
152 // load after configuration change.
153 static final Object sBgLock = new Object();
155 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
156 // LauncherModel to their ids
157 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
159 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
160 // created by LauncherModel that are directly on the home screen (however, no widgets or
161 // shortcuts within folders).
162 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
164 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
165 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
166 new ArrayList<LauncherAppWidgetInfo>();
168 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
169 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
171 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
172 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
174 // sBgWorkspaceScreens is the ordered set of workspace screens.
175 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
177 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
178 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
179 new HashMap<UserHandleCompat, HashSet<String>>();
181 // </ only access in worker thread >
183 private IconCache mIconCache;
185 protected int mPreviousConfigMcc;
187 private final LauncherAppsCompat mLauncherApps;
188 private final UserManagerCompat mUserManager;
190 public interface Callbacks {
191 public boolean setLoadOnResume();
192 public int getCurrentWorkspaceScreen();
193 public void startBinding();
194 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
195 boolean forceAnimateIcons);
196 public void bindScreens(ArrayList<Long> orderedScreenIds);
197 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
198 public void bindFolders(HashMap<Long,FolderInfo> folders);
199 public void finishBindingItems(boolean upgradePath);
200 public void bindAppWidget(LauncherAppWidgetInfo info);
201 public void bindAllApplications(ArrayList<AppInfo> apps);
202 public void bindAppsAdded(ArrayList<Long> newScreens,
203 ArrayList<ItemInfo> addNotAnimated,
204 ArrayList<ItemInfo> addAnimated,
205 ArrayList<AppInfo> addedApps);
206 public void bindAppsUpdated(ArrayList<AppInfo> apps);
207 public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
208 ArrayList<ShortcutInfo> removed, UserHandleCompat user);
209 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
210 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
211 public void updatePackageBadge(String packageName);
212 public void bindComponentsRemoved(ArrayList<String> packageNames,
213 ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
214 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
215 public void bindSearchablesChanged();
216 public boolean isAllAppsButtonRank(int rank);
217 public void onPageBoundSynchronously(int page);
218 public void dumpLogsToLocalData();
221 public interface ItemInfoFilter {
222 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
225 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
226 Context context = app.getContext();
228 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
229 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
230 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
232 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
233 ProviderInfo providerInfo =
234 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
235 ProviderInfo redirectProvider =
236 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
238 Log.d(TAG, "Old launcher provider: " + oldProvider);
239 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
241 if (mOldContentProviderExists) {
242 Log.d(TAG, "Old launcher provider exists.");
244 Log.d(TAG, "Old launcher provider does not exist.");
248 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
249 mIconCache = iconCache;
251 final Resources res = context.getResources();
252 Configuration config = res.getConfiguration();
253 mPreviousConfigMcc = config.mcc;
254 mLauncherApps = LauncherAppsCompat.getInstance(context);
255 mUserManager = UserManagerCompat.getInstance(context);
258 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
259 * posted on the main thread handler. */
260 private void runOnMainThread(Runnable r) {
261 runOnMainThread(r, 0);
263 private void runOnMainThread(Runnable r, int type) {
264 if (sWorkerThread.getThreadId() == Process.myTid()) {
265 // If we are on the worker thread, post onto the main handler
272 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
273 * posted on the worker thread handler. */
274 private static void runOnWorkerThread(Runnable r) {
275 if (sWorkerThread.getThreadId() == Process.myTid()) {
278 // If we are not on the worker thread, then post to the worker handler
283 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
284 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
287 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
289 LauncherAppState app = LauncherAppState.getInstance();
290 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
291 final int xCount = (int) grid.numColumns;
292 final int yCount = (int) grid.numRows;
293 boolean[][] occupied = new boolean[xCount][yCount];
295 int cellX, cellY, spanX, spanY;
296 for (int i = 0; i < items.size(); ++i) {
297 final ItemInfo item = items.get(i);
298 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
299 if (item.screenId == screen) {
304 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
305 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
306 occupied[x][y] = true;
313 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
315 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
317 int firstScreenIndex,
318 ArrayList<Long> workspaceScreens) {
319 // Lock on the app so that we don't try and get the items while apps are being added
320 LauncherAppState app = LauncherAppState.getInstance();
321 LauncherModel model = app.getModel();
322 boolean found = false;
324 if (sWorkerThread.getThreadId() != Process.myTid()) {
325 // Flush the LauncherModel worker thread, so that if we just did another
326 // processInstallShortcut, we give it time for its shortcut to get added to the
327 // database (getItemsInLocalCoordinates reads the database)
328 model.flushWorkerThread();
330 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
332 // Try adding to the workspace screens incrementally, starting at the default or center
333 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
334 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
335 int count = workspaceScreens.size();
336 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
337 int[] tmpCoordinates = new int[2];
338 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
339 workspaceScreens.get(screen))) {
340 // Update the Launcher db
341 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
348 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
349 // Process the updated package state
350 Runnable r = new Runnable() {
352 Callbacks callbacks = getCallback();
353 if (callbacks != null) {
354 callbacks.updatePackageState(installInfo);
361 public void updatePackageBadge(final String packageName) {
362 // Process the updated package badge
363 Runnable r = new Runnable() {
365 Callbacks callbacks = getCallback();
366 if (callbacks != null) {
367 callbacks.updatePackageBadge(packageName);
374 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
375 final Callbacks callbacks = getCallback();
377 if (allAppsApps == null) {
378 throw new RuntimeException("allAppsApps must not be null");
380 if (allAppsApps.isEmpty()) {
384 // Process the newly added applications and add them to the database first
385 Runnable r = new Runnable() {
387 runOnMainThread(new Runnable() {
389 Callbacks cb = getCallback();
390 if (callbacks == cb && cb != null) {
391 callbacks.bindAppsAdded(null, null, null, allAppsApps);
397 runOnWorkerThread(r);
400 public void addAndBindAddedWorkspaceApps(final Context context,
401 final ArrayList<ItemInfo> workspaceApps) {
402 final Callbacks callbacks = getCallback();
404 if (workspaceApps == null) {
405 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
407 if (workspaceApps.isEmpty()) {
410 // Process the newly added applications and add them to the database first
411 Runnable r = new Runnable() {
413 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
414 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
416 // Get the list of workspace screens. We need to append to this list and
417 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
419 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
420 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
421 for (Integer i : orderedScreens.keySet()) {
422 long screenId = orderedScreens.get(i);
423 workspaceScreens.add(screenId);
426 synchronized(sBgLock) {
427 Iterator<ItemInfo> iter = workspaceApps.iterator();
428 while (iter.hasNext()) {
429 ItemInfo a = iter.next();
430 final String name = a.title.toString();
431 final Intent launchIntent = a.getIntent();
433 // Short-circuit this logic if the icon exists somewhere on the workspace
434 if (shortcutExists(context, name, launchIntent, a.user)) {
438 // Add this icon to the db, creating a new page if necessary. If there
439 // is only the empty page then we just add items to the first page.
440 // Otherwise, we add them to the next pages.
441 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
442 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
443 name, launchIntent, startSearchPageIndex, workspaceScreens);
444 if (coords == null) {
445 LauncherProvider lp = LauncherAppState.getLauncherProvider();
447 // If we can't find a valid position, then just add a new screen.
448 // This takes time so we need to re-queue the add until the new
449 // page is added. Create as many screens as necessary to satisfy
450 // the startSearchPageIndex.
451 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
452 workspaceScreens.size());
453 while (numPagesToAdd > 0) {
454 long screenId = lp.generateNewScreenId();
455 // Save the screen id for binding in the workspace
456 workspaceScreens.add(screenId);
457 addedWorkspaceScreensFinal.add(screenId);
461 // Find the coordinate again
462 coords = LauncherModel.findNextAvailableIconSpace(context,
463 name, launchIntent, startSearchPageIndex, workspaceScreens);
465 if (coords == null) {
466 throw new RuntimeException("Coordinates should not be null");
469 ShortcutInfo shortcutInfo;
470 if (a instanceof ShortcutInfo) {
471 shortcutInfo = (ShortcutInfo) a;
472 } else if (a instanceof AppInfo) {
473 shortcutInfo = ((AppInfo) a).makeShortcut();
475 throw new RuntimeException("Unexpected info type");
478 // Add the shortcut to the db
479 addItemToDatabase(context, shortcutInfo,
480 LauncherSettings.Favorites.CONTAINER_DESKTOP,
481 coords.first, coords.second[0], coords.second[1], false);
482 // Save the ShortcutInfo for binding in the workspace
483 addedShortcutsFinal.add(shortcutInfo);
487 // Update the workspace screens
488 updateWorkspaceScreenOrder(context, workspaceScreens);
490 if (!addedShortcutsFinal.isEmpty()) {
491 runOnMainThread(new Runnable() {
493 Callbacks cb = getCallback();
494 if (callbacks == cb && cb != null) {
495 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
496 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
497 if (!addedShortcutsFinal.isEmpty()) {
498 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
499 long lastScreenId = info.screenId;
500 for (ItemInfo i : addedShortcutsFinal) {
501 if (i.screenId == lastScreenId) {
504 addNotAnimated.add(i);
508 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
509 addNotAnimated, addAnimated, null);
516 runOnWorkerThread(r);
519 public void unbindItemInfosAndClearQueuedBindRunnables() {
520 if (sWorkerThread.getThreadId() == Process.myTid()) {
521 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
525 // Clear any deferred bind runnables
526 synchronized (mDeferredBindRunnables) {
527 mDeferredBindRunnables.clear();
529 // Remove any queued bind runnables
530 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
531 // Unbind all the workspace items
532 unbindWorkspaceItemsOnMainThread();
535 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
536 void unbindWorkspaceItemsOnMainThread() {
537 // Ensure that we don't use the same workspace items data structure on the main thread
538 // by making a copy of workspace items first.
539 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
540 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
541 synchronized (sBgLock) {
542 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
543 tmpAppWidgets.addAll(sBgAppWidgets);
545 Runnable r = new Runnable() {
548 for (ItemInfo item : tmpWorkspaceItems) {
551 for (ItemInfo item : tmpAppWidgets) {
560 * Adds an item to the DB if it was not created previously, or move it to a new
561 * <container, screen, cellX, cellY>
563 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
564 long screenId, int cellX, int cellY) {
565 if (item.container == ItemInfo.NO_ID) {
567 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
569 // From somewhere else
570 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
574 static void checkItemInfoLocked(
575 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
576 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
577 if (modelItem != null && item != modelItem) {
578 // check all the data is consistent
579 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
580 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
581 ShortcutInfo shortcut = (ShortcutInfo) item;
582 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
583 modelShortcut.intent.filterEquals(shortcut.intent) &&
584 modelShortcut.id == shortcut.id &&
585 modelShortcut.itemType == shortcut.itemType &&
586 modelShortcut.container == shortcut.container &&
587 modelShortcut.screenId == shortcut.screenId &&
588 modelShortcut.cellX == shortcut.cellX &&
589 modelShortcut.cellY == shortcut.cellY &&
590 modelShortcut.spanX == shortcut.spanX &&
591 modelShortcut.spanY == shortcut.spanY &&
592 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
593 (modelShortcut.dropPos != null &&
594 shortcut.dropPos != null &&
595 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
596 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
597 // For all intents and purposes, this is the same object
602 // the modelItem needs to match up perfectly with item if our model is
603 // to be consistent with the database-- for now, just require
604 // modelItem == item or the equality check above
605 String msg = "item: " + ((item != null) ? item.toString() : "null") +
607 ((modelItem != null) ? modelItem.toString() : "null") +
608 "Error: ItemInfo passed to checkItemInfo doesn't match original";
609 RuntimeException e = new RuntimeException(msg);
610 if (stackTrace != null) {
611 e.setStackTrace(stackTrace);
617 static void checkItemInfo(final ItemInfo item) {
618 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
619 final long itemId = item.id;
620 Runnable r = new Runnable() {
622 synchronized (sBgLock) {
623 checkItemInfoLocked(itemId, item, stackTrace);
627 runOnWorkerThread(r);
630 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
631 final ItemInfo item, final String callingFunction) {
632 final long itemId = item.id;
633 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
634 final ContentResolver cr = context.getContentResolver();
636 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
637 Runnable r = new Runnable() {
639 cr.update(uri, values, null, null);
640 updateItemArrays(item, itemId, stackTrace);
643 runOnWorkerThread(r);
646 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
647 final ArrayList<ItemInfo> items, final String callingFunction) {
648 final ContentResolver cr = context.getContentResolver();
650 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
651 Runnable r = new Runnable() {
653 ArrayList<ContentProviderOperation> ops =
654 new ArrayList<ContentProviderOperation>();
655 int count = items.size();
656 for (int i = 0; i < count; i++) {
657 ItemInfo item = items.get(i);
658 final long itemId = item.id;
659 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
660 ContentValues values = valuesList.get(i);
662 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
663 updateItemArrays(item, itemId, stackTrace);
667 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
668 } catch (Exception e) {
673 runOnWorkerThread(r);
676 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
677 // Lock on mBgLock *after* the db operation
678 synchronized (sBgLock) {
679 checkItemInfoLocked(itemId, item, stackTrace);
681 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
682 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
683 // Item is in a folder, make sure this folder exists
684 if (!sBgFolders.containsKey(item.container)) {
685 // An items container is being set to a that of an item which is not in
686 // the list of Folders.
687 String msg = "item: " + item + " container being set to: " +
688 item.container + ", not in the list of folders";
693 // Items are added/removed from the corresponding FolderInfo elsewhere, such
694 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
695 // that are on the desktop, as appropriate
696 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
697 if (modelItem != null &&
698 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
699 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
700 switch (modelItem.itemType) {
701 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
702 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
703 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
704 if (!sBgWorkspaceItems.contains(modelItem)) {
705 sBgWorkspaceItems.add(modelItem);
712 sBgWorkspaceItems.remove(modelItem);
717 public void flushWorkerThread() {
718 mFlushingWorkerThread = true;
719 Runnable waiter = new Runnable() {
721 synchronized (this) {
723 mFlushingWorkerThread = false;
728 synchronized(waiter) {
729 runOnWorkerThread(waiter);
730 if (mLoaderTask != null) {
731 synchronized(mLoaderTask) {
732 mLoaderTask.notify();
735 boolean success = false;
740 } catch (InterruptedException e) {
747 * Move an item in the DB to a new <container, screen, cellX, cellY>
749 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
750 final long screenId, final int cellX, final int cellY) {
751 item.container = container;
755 // We store hotseat items in canonical form which is this orientation invariant position
757 if (context instanceof Launcher && screenId < 0 &&
758 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
759 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
761 item.screenId = screenId;
764 final ContentValues values = new ContentValues();
765 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
766 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
767 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
768 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
770 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
774 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
775 * cellX, cellY have already been updated on the ItemInfos.
777 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
778 final long container, final int screen) {
780 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
781 int count = items.size();
783 for (int i = 0; i < count; i++) {
784 ItemInfo item = items.get(i);
785 item.container = container;
787 // We store hotseat items in canonical form which is this orientation invariant position
789 if (context instanceof Launcher && screen < 0 &&
790 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
791 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
794 item.screenId = screen;
797 final ContentValues values = new ContentValues();
798 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
799 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
800 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
801 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
803 contentValues.add(values);
805 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
809 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
811 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
812 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
813 item.container = container;
819 // We store hotseat items in canonical form which is this orientation invariant position
821 if (context instanceof Launcher && screenId < 0 &&
822 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
823 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
825 item.screenId = screenId;
828 final ContentValues values = new ContentValues();
829 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
830 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
831 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
832 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
833 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
834 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
836 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
840 * Update an item to the database in a specified container.
842 static void updateItemInDatabase(Context context, final ItemInfo item) {
843 final ContentValues values = new ContentValues();
844 item.onAddToDatabase(context, values);
845 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
846 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
850 * Returns true if the shortcuts already exists in the database.
851 * we identify a shortcut by its title and intent.
853 static boolean shortcutExists(Context context, String title, Intent intent,
854 UserHandleCompat user) {
855 final ContentResolver cr = context.getContentResolver();
856 final Intent intentWithPkg, intentWithoutPkg;
858 if (intent.getComponent() != null) {
859 // If component is not null, an intent with null package will produce
860 // the same result and should also be a match.
861 if (intent.getPackage() != null) {
862 intentWithPkg = intent;
863 intentWithoutPkg = new Intent(intent).setPackage(null);
865 intentWithPkg = new Intent(intent).setPackage(
866 intent.getComponent().getPackageName());
867 intentWithoutPkg = intent;
870 intentWithPkg = intent;
871 intentWithoutPkg = intent;
873 String userSerial = Long.toString(UserManagerCompat.getInstance(context)
874 .getSerialNumberForUser(user));
875 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
876 new String[] { "title", "intent", "profileId" },
877 "title=? and (intent=? or intent=?) and profileId=?",
878 new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial },
881 return c.moveToFirst();
888 * Returns an ItemInfo array containing all the items in the LauncherModel.
889 * The ItemInfo.id is not set through this function.
891 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
892 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
893 final ContentResolver cr = context.getContentResolver();
894 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
895 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
896 LauncherSettings.Favorites.SCREEN,
897 LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
898 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
899 LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
901 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
902 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
903 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
904 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
905 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
906 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
907 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
908 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
909 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
911 while (c.moveToNext()) {
912 ItemInfo item = new ItemInfo();
913 item.cellX = c.getInt(cellXIndex);
914 item.cellY = c.getInt(cellYIndex);
915 item.spanX = Math.max(1, c.getInt(spanXIndex));
916 item.spanY = Math.max(1, c.getInt(spanYIndex));
917 item.container = c.getInt(containerIndex);
918 item.itemType = c.getInt(itemTypeIndex);
919 item.screenId = c.getInt(screenIndex);
920 long serialNumber = c.getInt(profileIdIndex);
921 item.user = userManager.getUserForSerialNumber(serialNumber);
922 // Skip if user has been deleted.
923 if (item.user != null) {
927 } catch (Exception e) {
937 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
939 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
940 final ContentResolver cr = context.getContentResolver();
941 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
942 "_id=? and (itemType=? or itemType=?)",
943 new String[] { String.valueOf(id),
944 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
947 if (c.moveToFirst()) {
948 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
949 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
950 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
951 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
952 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
953 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
955 FolderInfo folderInfo = null;
956 switch (c.getInt(itemTypeIndex)) {
957 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
958 folderInfo = findOrMakeFolder(folderList, id);
962 folderInfo.title = c.getString(titleIndex);
964 folderInfo.container = c.getInt(containerIndex);
965 folderInfo.screenId = c.getInt(screenIndex);
966 folderInfo.cellX = c.getInt(cellXIndex);
967 folderInfo.cellY = c.getInt(cellYIndex);
979 * Add an item to the database in a specified container. Sets the container, screen, cellX and
980 * cellY fields of the item. Also assigns an ID to the item.
982 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
983 final long screenId, final int cellX, final int cellY, final boolean notify) {
984 item.container = container;
987 // We store hotseat items in canonical form which is this orientation invariant position
989 if (context instanceof Launcher && screenId < 0 &&
990 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
991 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
993 item.screenId = screenId;
996 final ContentValues values = new ContentValues();
997 final ContentResolver cr = context.getContentResolver();
998 item.onAddToDatabase(context, values);
1000 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
1001 values.put(LauncherSettings.Favorites._ID, item.id);
1002 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1004 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1005 Runnable r = new Runnable() {
1007 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1008 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1010 // Lock on mBgLock *after* the db operation
1011 synchronized (sBgLock) {
1012 checkItemInfoLocked(item.id, item, stackTrace);
1013 sBgItemsIdMap.put(item.id, item);
1014 switch (item.itemType) {
1015 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1016 sBgFolders.put(item.id, (FolderInfo) item);
1018 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1019 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1020 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1021 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1022 sBgWorkspaceItems.add(item);
1024 if (!sBgFolders.containsKey(item.container)) {
1025 // Adding an item to a folder that doesn't exist.
1026 String msg = "adding item: " + item + " to a folder that " +
1032 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1033 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
1039 runOnWorkerThread(r);
1043 * Creates a new unique child id, for a given cell span across all layouts.
1045 static int getCellLayoutChildId(
1046 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
1047 return (((int) container & 0xFF) << 24)
1048 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
1051 private static ArrayList<ItemInfo> getItemsByPackageName(
1052 final String pn, final UserHandleCompat user) {
1053 ItemInfoFilter filter = new ItemInfoFilter() {
1055 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1056 return cn.getPackageName().equals(pn) && info.user.equals(user);
1059 return filterItemInfos(sBgItemsIdMap.values(), filter);
1063 * Removes all the items from the database corresponding to the specified package.
1065 static void deletePackageFromDatabase(Context context, final String pn,
1066 final UserHandleCompat user) {
1067 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
1071 * Removes the specified item from the database
1075 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
1076 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1078 deleteItemsFromDatabase(context, items);
1082 * Removes the specified items from the database
1086 static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
1087 final ContentResolver cr = context.getContentResolver();
1089 Runnable r = new Runnable() {
1091 for (ItemInfo item : items) {
1092 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1093 cr.delete(uri, null, null);
1095 // Lock on mBgLock *after* the db operation
1096 synchronized (sBgLock) {
1097 switch (item.itemType) {
1098 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1099 sBgFolders.remove(item.id);
1100 for (ItemInfo info: sBgItemsIdMap.values()) {
1101 if (info.container == item.id) {
1102 // We are deleting a folder which still contains items that
1103 // think they are contained by that folder.
1104 String msg = "deleting a folder (" + item + ") which still " +
1105 "contains items (" + info + ")";
1109 sBgWorkspaceItems.remove(item);
1111 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1112 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1113 sBgWorkspaceItems.remove(item);
1115 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1116 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1119 sBgItemsIdMap.remove(item.id);
1120 sBgDbIconCache.remove(item);
1125 runOnWorkerThread(r);
1129 * Update the order of the workspace screens in the database. The array list contains
1130 * a list of screen ids in the order that they should appear.
1132 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
1134 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1135 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1137 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
1138 final ContentResolver cr = context.getContentResolver();
1139 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1141 // Remove any negative screen ids -- these aren't persisted
1142 Iterator<Long> iter = screensCopy.iterator();
1143 while (iter.hasNext()) {
1144 long id = iter.next();
1150 Runnable r = new Runnable() {
1153 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1155 ops.add(ContentProviderOperation.newDelete(uri).build());
1156 int count = screensCopy.size();
1157 for (int i = 0; i < count; i++) {
1158 ContentValues v = new ContentValues();
1159 long screenId = screensCopy.get(i);
1160 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1161 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1162 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
1166 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1167 } catch (Exception ex) {
1168 throw new RuntimeException(ex);
1171 synchronized (sBgLock) {
1172 sBgWorkspaceScreens.clear();
1173 sBgWorkspaceScreens.addAll(screensCopy);
1177 runOnWorkerThread(r);
1181 * Remove the contents of the specified folder from the database
1183 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
1184 final ContentResolver cr = context.getContentResolver();
1186 Runnable r = new Runnable() {
1188 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1189 // Lock on mBgLock *after* the db operation
1190 synchronized (sBgLock) {
1191 sBgItemsIdMap.remove(info.id);
1192 sBgFolders.remove(info.id);
1193 sBgDbIconCache.remove(info);
1194 sBgWorkspaceItems.remove(info);
1197 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1198 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1199 // Lock on mBgLock *after* the db operation
1200 synchronized (sBgLock) {
1201 for (ItemInfo childInfo : info.contents) {
1202 sBgItemsIdMap.remove(childInfo.id);
1203 sBgDbIconCache.remove(childInfo);
1208 runOnWorkerThread(r);
1212 * Set this as the current Launcher activity object for the loader.
1214 public void initialize(Callbacks callbacks) {
1215 synchronized (mLock) {
1216 mCallbacks = new WeakReference<Callbacks>(callbacks);
1221 public void onPackageChanged(String packageName, UserHandleCompat user) {
1222 int op = PackageUpdatedTask.OP_UPDATE;
1223 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1228 public void onPackageRemoved(String packageName, UserHandleCompat user) {
1229 int op = PackageUpdatedTask.OP_REMOVE;
1230 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1235 public void onPackageAdded(String packageName, UserHandleCompat user) {
1236 int op = PackageUpdatedTask.OP_ADD;
1237 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1242 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
1243 boolean replacing) {
1245 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
1247 if (mAppsCanBeOnRemoveableStorage) {
1248 // Only rebind if we support removable storage. It catches the
1250 // apps on the external sd card need to be reloaded
1251 startLoaderFromBackground();
1254 // If we are replacing then just update the packages in the list
1255 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1256 packageNames, user));
1261 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
1262 boolean replacing) {
1264 enqueuePackageUpdated(new PackageUpdatedTask(
1265 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1272 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1273 * ACTION_PACKAGE_CHANGED.
1276 public void onReceive(Context context, Intent intent) {
1277 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
1279 final String action = intent.getAction();
1280 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1281 // If we have changed locale we need to clear out the labels in all apps/workspace.
1283 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1284 // Check if configuration change was an mcc/mnc change which would affect app resources
1285 // and we would need to clear out the labels in all apps/workspace. Same handling as
1286 // above for ACTION_LOCALE_CHANGED
1287 Configuration currentConfig = context.getResources().getConfiguration();
1288 if (mPreviousConfigMcc != currentConfig.mcc) {
1289 Log.d(TAG, "Reload apps on config change. curr_mcc:"
1290 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
1293 // Update previousConfig
1294 mPreviousConfigMcc = currentConfig.mcc;
1295 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1296 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
1297 Callbacks callbacks = getCallback();
1298 if (callbacks != null) {
1299 callbacks.bindSearchablesChanged();
1304 void forceReload() {
1305 resetLoadedState(true, true);
1307 // Do this here because if the launcher activity is running it will be restarted.
1308 // If it's not running startLoaderFromBackground will merely tell it that it needs
1310 startLoaderFromBackground();
1313 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1314 synchronized (mLock) {
1315 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1316 // mWorkspaceLoaded to true later
1318 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1319 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1324 * When the launcher is in the background, it's possible for it to miss paired
1325 * configuration changes. So whenever we trigger the loader from the background
1326 * tell the launcher that it needs to re-run the loader when it comes back instead
1329 public void startLoaderFromBackground() {
1330 boolean runLoader = false;
1331 Callbacks callbacks = getCallback();
1332 if (callbacks != null) {
1333 // Only actually run the loader if they're not paused.
1334 if (!callbacks.setLoadOnResume()) {
1339 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
1343 // If there is already a loader task running, tell it to stop.
1344 // returns true if isLaunching() was true on the old task
1345 private boolean stopLoaderLocked() {
1346 boolean isLaunching = false;
1347 LoaderTask oldTask = mLoaderTask;
1348 if (oldTask != null) {
1349 if (oldTask.isLaunching()) {
1352 oldTask.stopLocked();
1357 public boolean isCurrentCallbacks(Callbacks callbacks) {
1358 return (mCallbacks != null && mCallbacks.get() == callbacks);
1361 public void startLoader(boolean isLaunching, int synchronousBindPage) {
1362 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1365 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
1366 synchronized (mLock) {
1367 if (DEBUG_LOADERS) {
1368 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1371 // Clear any deferred bind-runnables from the synchronized load process
1372 // We must do this before any loading/binding is scheduled below.
1373 synchronized (mDeferredBindRunnables) {
1374 mDeferredBindRunnables.clear();
1377 // Don't bother to start the thread if we know it's not going to do anything
1378 if (mCallbacks != null && mCallbacks.get() != null) {
1379 // If there is already one running, tell it to stop.
1380 // also, don't downgrade isLaunching if we're already running
1381 isLaunching = isLaunching || stopLoaderLocked();
1382 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
1383 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1384 && mAllAppsLoaded && mWorkspaceLoaded) {
1385 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1387 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1388 sWorker.post(mLoaderTask);
1394 void bindRemainingSynchronousPages() {
1395 // Post the remaining side pages to be loaded
1396 if (!mDeferredBindRunnables.isEmpty()) {
1397 Runnable[] deferredBindRunnables = null;
1398 synchronized (mDeferredBindRunnables) {
1399 deferredBindRunnables = mDeferredBindRunnables.toArray(
1400 new Runnable[mDeferredBindRunnables.size()]);
1401 mDeferredBindRunnables.clear();
1403 for (final Runnable r : deferredBindRunnables) {
1404 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
1409 public void stopLoader() {
1410 synchronized (mLock) {
1411 if (mLoaderTask != null) {
1412 mLoaderTask.stopLocked();
1417 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1418 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1419 final ContentResolver contentResolver = context.getContentResolver();
1420 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1421 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1422 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1425 final int idIndex = sc.getColumnIndexOrThrow(
1426 LauncherSettings.WorkspaceScreens._ID);
1427 final int rankIndex = sc.getColumnIndexOrThrow(
1428 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1429 while (sc.moveToNext()) {
1431 long screenId = sc.getLong(idIndex);
1432 int rank = sc.getInt(rankIndex);
1433 orderedScreens.put(rank, screenId);
1434 } catch (Exception e) {
1435 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true);
1443 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1444 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1445 for (Integer i : orderedScreens.keySet()) {
1446 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1448 Launcher.addDumpLog(TAG, "11683562 - screens: " +
1449 TextUtils.join(", ", orderedScreensPairs), true);
1450 return orderedScreens;
1453 public boolean isAllAppsLoaded() {
1454 return mAllAppsLoaded;
1457 boolean isLoadingWorkspace() {
1458 synchronized (mLock) {
1459 if (mLoaderTask != null) {
1460 return mLoaderTask.isLoadingWorkspace();
1467 * Runnable for the thread that loads the contents of the launcher:
1472 private class LoaderTask implements Runnable {
1473 private Context mContext;
1474 private boolean mIsLaunching;
1475 private boolean mIsLoadingAndBindingWorkspace;
1476 private boolean mStopped;
1477 private boolean mLoadAndBindStepFinished;
1480 private HashMap<Object, CharSequence> mLabelCache;
1482 LoaderTask(Context context, boolean isLaunching, int flags) {
1484 mIsLaunching = isLaunching;
1485 mLabelCache = new HashMap<Object, CharSequence>();
1489 boolean isLaunching() {
1490 return mIsLaunching;
1493 boolean isLoadingWorkspace() {
1494 return mIsLoadingAndBindingWorkspace;
1497 /** Returns whether this is an upgrade path */
1498 private boolean loadAndBindWorkspace() {
1499 mIsLoadingAndBindingWorkspace = true;
1501 // Load the workspace
1502 if (DEBUG_LOADERS) {
1503 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
1506 boolean isUpgradePath = false;
1507 if (!mWorkspaceLoaded) {
1508 isUpgradePath = loadWorkspace();
1509 synchronized (LoaderTask.this) {
1511 return isUpgradePath;
1513 mWorkspaceLoaded = true;
1517 // Bind the workspace
1518 bindWorkspace(-1, isUpgradePath);
1519 return isUpgradePath;
1522 private void waitForIdle() {
1523 // Wait until the either we're stopped or the other threads are done.
1524 // This way we don't start loading all apps until the workspace has settled
1526 synchronized (LoaderTask.this) {
1527 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1529 mHandler.postIdle(new Runnable() {
1531 synchronized (LoaderTask.this) {
1532 mLoadAndBindStepFinished = true;
1533 if (DEBUG_LOADERS) {
1534 Log.d(TAG, "done with previous binding step");
1536 LoaderTask.this.notify();
1541 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
1543 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1544 // wait no longer than 1sec at a time
1546 } catch (InterruptedException ex) {
1550 if (DEBUG_LOADERS) {
1551 Log.d(TAG, "waited "
1552 + (SystemClock.uptimeMillis()-workspaceWaitTime)
1553 + "ms for previous step to finish binding");
1558 void runBindSynchronousPage(int synchronousBindPage) {
1559 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
1560 // Ensure that we have a valid page index to load synchronously
1561 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1562 "valid page index");
1564 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1565 // Ensure that we don't try and bind a specified page when the pages have not been
1566 // loaded already (we should load everything asynchronously in that case)
1567 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1569 synchronized (mLock) {
1570 if (mIsLoaderTaskRunning) {
1571 // Ensure that we are never running the background loading at this point since
1572 // we also touch the background collections
1573 throw new RuntimeException("Error! Background loading is already running");
1577 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1578 // data structures, we can't allow any other thread to touch that data, but because
1579 // this call is synchronous, we can get away with not locking).
1581 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
1582 // operations from the previous activity. We need to ensure that all queued operations
1583 // are executed before any synchronous binding work is done.
1586 // Divide the set of loaded items into those that we are binding synchronously, and
1587 // everything else that is to be bound normally (asynchronously).
1588 bindWorkspace(synchronousBindPage, false);
1589 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1595 boolean isUpgrade = false;
1597 synchronized (mLock) {
1598 mIsLoaderTaskRunning = true;
1600 // Optimize for end-user experience: if the Launcher is up and // running with the
1601 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1602 // workspace first (default).
1604 // Elevate priority when Home launches for the first time to avoid
1605 // starving at boot time. Staring at a blank home is not cool.
1606 synchronized (mLock) {
1607 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
1608 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
1609 android.os.Process.setThreadPriority(mIsLaunching
1610 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
1612 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
1613 isUpgrade = loadAndBindWorkspace();
1619 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1621 synchronized (mLock) {
1623 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
1624 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1630 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1631 loadAndBindAllApps();
1633 // Restore the default thread priority after we are done loading items
1634 synchronized (mLock) {
1635 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1639 // Update the saved icons if necessary
1640 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
1641 synchronized (sBgLock) {
1642 for (Object key : sBgDbIconCache.keySet()) {
1643 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
1645 sBgDbIconCache.clear();
1648 if (LauncherAppState.isDisableAllApps()) {
1649 // Ensure that all the applications that are in the system are
1650 // represented on the home screen.
1651 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
1652 verifyApplications();
1656 // Clear out this reference, otherwise we end up holding it until all of the
1657 // callback runnables are done.
1660 synchronized (mLock) {
1661 // If we are still the last one to be scheduled, remove ourselves.
1662 if (mLoaderTask == this) {
1665 mIsLoaderTaskRunning = false;
1669 public void stopLocked() {
1670 synchronized (LoaderTask.this) {
1677 * Gets the callbacks object. If we've been stopped, or if the launcher object
1678 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1679 * object that was around when the deferred message was scheduled, and if there's
1680 * a new Callbacks object around then also return null. This will save us from
1681 * calling onto it with data that will be ignored.
1683 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1684 synchronized (mLock) {
1689 if (mCallbacks == null) {
1693 final Callbacks callbacks = mCallbacks.get();
1694 if (callbacks != oldCallbacks) {
1697 if (callbacks == null) {
1698 Log.w(TAG, "no mCallbacks");
1706 private void verifyApplications() {
1707 final Context context = mApp.getContext();
1709 // Cross reference all the applications in our apps list with items in the workspace
1710 ArrayList<ItemInfo> tmpInfos;
1711 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
1712 synchronized (sBgLock) {
1713 for (AppInfo app : mBgAllAppsList.data) {
1714 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
1715 if (tmpInfos.isEmpty()) {
1716 // We are missing an application icon, so add this to the workspace
1718 // This is a rare event, so lets log it
1719 Log.e(TAG, "Missing Application on load: " + app);
1723 if (!added.isEmpty()) {
1724 addAndBindAddedWorkspaceApps(context, added);
1728 // check & update map of what's occupied; used to discard overlapping/invalid items
1729 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item) {
1730 LauncherAppState app = LauncherAppState.getInstance();
1731 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1732 final int countX = (int) grid.numColumns;
1733 final int countY = (int) grid.numRows;
1735 long containerIndex = item.screenId;
1736 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1737 // Return early if we detect that an item is under the hotseat button
1738 if (mCallbacks == null ||
1739 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
1740 Log.e(TAG, "Error loading shortcut into hotseat " + item
1741 + " into position (" + item.screenId + ":" + item.cellX + ","
1742 + item.cellY + ") occupied by all apps");
1746 final ItemInfo[][] hotseatItems =
1747 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1749 if (item.screenId >= grid.numHotseatIcons) {
1750 Log.e(TAG, "Error loading shortcut " + item
1751 + " into hotseat position " + item.screenId
1752 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
1757 if (hotseatItems != null) {
1758 if (hotseatItems[(int) item.screenId][0] != null) {
1759 Log.e(TAG, "Error loading shortcut into hotseat " + item
1760 + " into position (" + item.screenId + ":" + item.cellX + ","
1761 + item.cellY + ") occupied by "
1762 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1763 [(int) item.screenId][0]);
1766 hotseatItems[(int) item.screenId][0] = item;
1770 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
1771 items[(int) item.screenId][0] = item;
1772 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
1775 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1776 // Skip further checking if it is not the hotseat or workspace container
1780 if (!occupied.containsKey(item.screenId)) {
1781 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
1782 occupied.put(item.screenId, items);
1785 final ItemInfo[][] screens = occupied.get(item.screenId);
1786 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1787 item.cellX < 0 || item.cellY < 0 ||
1788 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1789 Log.e(TAG, "Error loading shortcut " + item
1790 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1791 + item.cellX + "," + item.cellY
1792 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1796 // Check if any workspace icons overlap with each other
1797 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1798 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1799 if (screens[x][y] != null) {
1800 Log.e(TAG, "Error loading shortcut " + item
1801 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1809 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1810 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1811 screens[x][y] = item;
1818 /** Clears all the sBg data structures */
1819 private void clearSBgDataStructures() {
1820 synchronized (sBgLock) {
1821 sBgWorkspaceItems.clear();
1822 sBgAppWidgets.clear();
1824 sBgItemsIdMap.clear();
1825 sBgDbIconCache.clear();
1826 sBgWorkspaceScreens.clear();
1830 /** Returns whether this is an upgrade path */
1831 private boolean loadWorkspace() {
1833 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1835 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1837 final Context context = mContext;
1838 final ContentResolver contentResolver = context.getContentResolver();
1839 final PackageManager manager = context.getPackageManager();
1840 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1841 final boolean isSafeMode = manager.isSafeMode();
1842 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
1843 final boolean isSdCardReady = context.registerReceiver(null,
1844 new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
1846 LauncherAppState app = LauncherAppState.getInstance();
1847 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1848 int countX = (int) grid.numColumns;
1849 int countY = (int) grid.numRows;
1851 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1852 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1853 LauncherAppState.getLauncherProvider().deleteDatabase();
1856 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1857 // append the user's Launcher2 shortcuts
1858 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1859 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1861 // Make sure the default workspace is loaded
1862 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
1863 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
1866 // This code path is for our old migration code and should no longer be exercised
1867 boolean loadedOldDb = false;
1870 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
1872 synchronized (sBgLock) {
1873 clearSBgDataStructures();
1874 final HashSet<String> installingPkgs = PackageInstallerCompat
1875 .getInstance(mContext).updateAndGetActiveSessionCache();
1877 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
1878 final ArrayList<Long> restoredRows = new ArrayList<Long>();
1879 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
1880 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
1881 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
1883 // +1 for the hotseat (it can be larger than the workspace)
1884 // Load workspace in reverse order to ensure that latest items are loaded first (and
1885 // before any earlier duplicates)
1886 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
1889 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1890 final int intentIndex = c.getColumnIndexOrThrow
1891 (LauncherSettings.Favorites.INTENT);
1892 final int titleIndex = c.getColumnIndexOrThrow
1893 (LauncherSettings.Favorites.TITLE);
1894 final int iconTypeIndex = c.getColumnIndexOrThrow(
1895 LauncherSettings.Favorites.ICON_TYPE);
1896 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1897 final int iconPackageIndex = c.getColumnIndexOrThrow(
1898 LauncherSettings.Favorites.ICON_PACKAGE);
1899 final int iconResourceIndex = c.getColumnIndexOrThrow(
1900 LauncherSettings.Favorites.ICON_RESOURCE);
1901 final int containerIndex = c.getColumnIndexOrThrow(
1902 LauncherSettings.Favorites.CONTAINER);
1903 final int itemTypeIndex = c.getColumnIndexOrThrow(
1904 LauncherSettings.Favorites.ITEM_TYPE);
1905 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1906 LauncherSettings.Favorites.APPWIDGET_ID);
1907 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1908 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
1909 final int screenIndex = c.getColumnIndexOrThrow(
1910 LauncherSettings.Favorites.SCREEN);
1911 final int cellXIndex = c.getColumnIndexOrThrow
1912 (LauncherSettings.Favorites.CELLX);
1913 final int cellYIndex = c.getColumnIndexOrThrow
1914 (LauncherSettings.Favorites.CELLY);
1915 final int spanXIndex = c.getColumnIndexOrThrow
1916 (LauncherSettings.Favorites.SPANX);
1917 final int spanYIndex = c.getColumnIndexOrThrow(
1918 LauncherSettings.Favorites.SPANY);
1919 final int restoredIndex = c.getColumnIndexOrThrow(
1920 LauncherSettings.Favorites.RESTORED);
1921 final int profileIdIndex = c.getColumnIndexOrThrow(
1922 LauncherSettings.Favorites.PROFILE_ID);
1923 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1924 //final int displayModeIndex = c.getColumnIndexOrThrow(
1925 // LauncherSettings.Favorites.DISPLAY_MODE);
1928 String intentDescription;
1929 LauncherAppWidgetInfo appWidgetInfo;
1933 UserHandleCompat user;
1935 while (!mStopped && c.moveToNext()) {
1937 int itemType = c.getInt(itemTypeIndex);
1938 boolean restored = 0 != c.getInt(restoredIndex);
1939 boolean allowMissingTarget = false;
1942 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1943 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1944 id = c.getLong(idIndex);
1945 intentDescription = c.getString(intentIndex);
1946 long serialNumber = c.getInt(profileIdIndex);
1947 user = mUserManager.getUserForSerialNumber(serialNumber);
1948 int promiseType = c.getInt(restoredIndex);
1949 int disabledState = 0;
1951 // User has been deleted remove the item.
1952 itemsToRemove.add(id);
1956 intent = Intent.parseUri(intentDescription, 0);
1957 ComponentName cn = intent.getComponent();
1958 if (cn != null && cn.getPackageName() != null) {
1959 boolean validPkg = launcherApps.isPackageEnabledForProfile(
1960 cn.getPackageName(), user);
1961 boolean validComponent = validPkg &&
1962 launcherApps.isActivityEnabledForProfile(cn, user);
1964 if (validComponent) {
1966 // no special handling necessary for this item
1967 restoredRows.add(id);
1970 } else if (validPkg) {
1972 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
1973 // We allow auto install apps to have their intent
1974 // updated after an install.
1975 intent = manager.getLaunchIntentForPackage(
1976 cn.getPackageName());
1977 if (intent != null) {
1978 ContentValues values = new ContentValues();
1979 values.put(LauncherSettings.Favorites.INTENT,
1981 String where = BaseColumns._ID + "= ?";
1982 String[] args = {Long.toString(id)};
1983 contentResolver.update(contentUri, values, where, args);
1987 if (intent == null) {
1988 // The app is installed but the component is no
1989 // longer available.
1990 Launcher.addDumpLog(TAG,
1991 "Invalid component removed: " + cn, true);
1992 itemsToRemove.add(id);
1995 // no special handling necessary for this item
1996 restoredRows.add(id);
1999 } else if (restored) {
2000 // Package is not yet available but might be
2002 Launcher.addDumpLog(TAG,
2003 "package not yet restored: " + cn, true);
2005 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
2006 // Restore has started once.
2007 } else if (installingPkgs.contains(cn.getPackageName())) {
2008 // App restore has started. Update the flag
2009 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
2010 ContentValues values = new ContentValues();
2011 values.put(LauncherSettings.Favorites.RESTORED,
2013 String where = BaseColumns._ID + "= ?";
2014 String[] args = {Long.toString(id)};
2015 contentResolver.update(contentUri, values, where, args);
2017 } else if (REMOVE_UNRESTORED_ICONS) {
2018 Launcher.addDumpLog(TAG,
2019 "Unrestored package removed: " + cn, true);
2020 itemsToRemove.add(id);
2023 } else if (launcherApps.isAppEnabled(
2024 manager, cn.getPackageName(),
2025 PackageManager.GET_UNINSTALLED_PACKAGES)) {
2026 // Package is present but not available.
2027 allowMissingTarget = true;
2028 disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
2029 } else if (!isSdCardReady) {
2030 // SdCard is not ready yet. Package might get available,
2031 // once it is ready.
2032 Launcher.addDumpLog(TAG, "Invalid package: " + cn
2033 + " (check again later)", true);
2034 HashSet<String> pkgs = sPendingPackages.get(user);
2036 pkgs = new HashSet<String>();
2037 sPendingPackages.put(user, pkgs);
2039 pkgs.add(cn.getPackageName());
2040 allowMissingTarget = true;
2041 // Add the icon on the workspace anyway.
2044 // Do not wait for external media load anymore.
2045 // Log the invalid package, and remove it
2046 Launcher.addDumpLog(TAG,
2047 "Invalid package removed: " + cn, true);
2048 itemsToRemove.add(id);
2051 } else if (cn == null) {
2052 // For shortcuts with no component, keep them as they are
2053 restoredRows.add(id);
2056 } catch (URISyntaxException e) {
2057 Launcher.addDumpLog(TAG,
2058 "Invalid uri: " + intentDescription, true);
2063 if (user.equals(UserHandleCompat.myUserHandle())) {
2064 Launcher.addDumpLog(TAG,
2065 "constructing info for partially restored package",
2067 info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
2068 intent = getRestoredItemIntent(c, context, intent);
2070 // Don't restore items for other profiles.
2071 itemsToRemove.add(id);
2074 } else if (itemType ==
2075 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2076 info = getShortcutInfo(manager, intent, user, context, c,
2077 iconIndex, titleIndex, mLabelCache, allowMissingTarget);
2079 info = getShortcutInfo(c, context, iconTypeIndex,
2080 iconPackageIndex, iconResourceIndex, iconIndex,
2083 // App shortcuts that used to be automatically added to Launcher
2084 // didn't always have the correct intent flags set, so do that
2086 if (intent.getAction() != null &&
2087 intent.getCategories() != null &&
2088 intent.getAction().equals(Intent.ACTION_MAIN) &&
2089 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
2091 Intent.FLAG_ACTIVITY_NEW_TASK |
2092 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2098 info.intent = intent;
2099 container = c.getInt(containerIndex);
2100 info.container = container;
2101 info.screenId = c.getInt(screenIndex);
2102 info.cellX = c.getInt(cellXIndex);
2103 info.cellY = c.getInt(cellYIndex);
2106 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
2107 info.isDisabled = disabledState;
2108 if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
2109 info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
2112 // check & update map of what's occupied
2113 if (!checkItemPlacement(occupied, info)) {
2114 itemsToRemove.add(id);
2118 switch (container) {
2119 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2120 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2121 sBgWorkspaceItems.add(info);
2124 // Item is in a user folder
2125 FolderInfo folderInfo =
2126 findOrMakeFolder(sBgFolders, container);
2127 folderInfo.add(info);
2130 sBgItemsIdMap.put(info.id, info);
2132 // now that we've loaded everthing re-save it with the
2133 // icon in case it disappears somehow.
2134 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
2136 throw new RuntimeException("Unexpected null ShortcutInfo");
2140 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2141 id = c.getLong(idIndex);
2142 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2144 folderInfo.title = c.getString(titleIndex);
2146 container = c.getInt(containerIndex);
2147 folderInfo.container = container;
2148 folderInfo.screenId = c.getInt(screenIndex);
2149 folderInfo.cellX = c.getInt(cellXIndex);
2150 folderInfo.cellY = c.getInt(cellYIndex);
2151 folderInfo.spanX = 1;
2152 folderInfo.spanY = 1;
2154 // check & update map of what's occupied
2155 if (!checkItemPlacement(occupied, folderInfo)) {
2156 itemsToRemove.add(id);
2160 switch (container) {
2161 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2162 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2163 sBgWorkspaceItems.add(folderInfo);
2168 // no special handling required for restored folders
2169 restoredRows.add(id);
2172 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2173 sBgFolders.put(folderInfo.id, folderInfo);
2176 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2177 // Read all Launcher-specific widget details
2178 int appWidgetId = c.getInt(appWidgetIdIndex);
2179 String savedProvider = c.getString(appWidgetProviderIndex);
2180 id = c.getLong(idIndex);
2181 final ComponentName component =
2182 ComponentName.unflattenFromString(savedProvider);
2184 final int restoreStatus = c.getInt(restoredIndex);
2185 final boolean isIdValid = (restoreStatus &
2186 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
2188 final boolean wasProviderReady = (restoreStatus &
2189 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
2191 final AppWidgetProviderInfo provider = isIdValid
2192 ? widgets.getAppWidgetInfo(appWidgetId)
2193 : findAppWidgetProviderInfoWithComponent(context, component);
2195 final boolean isProviderReady = isValidProvider(provider);
2196 if (!isSafeMode && wasProviderReady && !isProviderReady) {
2197 String log = "Deleting widget that isn't installed anymore: "
2198 + "id=" + id + " appWidgetId=" + appWidgetId;
2200 Launcher.addDumpLog(TAG, log, false);
2201 itemsToRemove.add(id);
2203 if (isProviderReady) {
2204 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2207 Launcher.getMinSpanForWidget(context, provider);
2208 appWidgetInfo.minSpanX = minSpan[0];
2209 appWidgetInfo.minSpanY = minSpan[1];
2211 int status = restoreStatus;
2212 if (!wasProviderReady) {
2213 // If provider was not previously ready, update the
2214 // status and UI flag.
2216 // Id would be valid only if the widget restore broadcast was received.
2218 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
2220 status &= ~LauncherAppWidgetInfo
2221 .FLAG_PROVIDER_NOT_READY;
2224 appWidgetInfo.restoreStatus = status;
2226 Log.v(TAG, "Widget restore pending id=" + id
2227 + " appWidgetId=" + appWidgetId
2228 + " status =" + restoreStatus);
2229 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2231 appWidgetInfo.restoreStatus = restoreStatus;
2233 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
2234 // Restore has started once.
2235 } else if (installingPkgs.contains(component.getPackageName())) {
2236 // App restore has started. Update the flag
2237 appWidgetInfo.restoreStatus |=
2238 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
2239 } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
2240 Launcher.addDumpLog(TAG,
2241 "Unrestored widget removed: " + component, true);
2242 itemsToRemove.add(id);
2247 appWidgetInfo.id = id;
2248 appWidgetInfo.screenId = c.getInt(screenIndex);
2249 appWidgetInfo.cellX = c.getInt(cellXIndex);
2250 appWidgetInfo.cellY = c.getInt(cellYIndex);
2251 appWidgetInfo.spanX = c.getInt(spanXIndex);
2252 appWidgetInfo.spanY = c.getInt(spanYIndex);
2254 container = c.getInt(containerIndex);
2255 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2256 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2257 Log.e(TAG, "Widget found where container != " +
2258 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2262 appWidgetInfo.container = c.getInt(containerIndex);
2263 // check & update map of what's occupied
2264 if (!checkItemPlacement(occupied, appWidgetInfo)) {
2265 itemsToRemove.add(id);
2269 String providerName = appWidgetInfo.providerName.flattenToString();
2270 if (!providerName.equals(savedProvider) ||
2271 (appWidgetInfo.restoreStatus != restoreStatus)) {
2272 ContentValues values = new ContentValues();
2273 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2275 values.put(LauncherSettings.Favorites.RESTORED,
2276 appWidgetInfo.restoreStatus);
2277 String where = BaseColumns._ID + "= ?";
2278 String[] args = {Long.toString(id)};
2279 contentResolver.update(contentUri, values, where, args);
2281 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2282 sBgAppWidgets.add(appWidgetInfo);
2286 } catch (Exception e) {
2287 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
2296 // Break early if we've stopped loading
2298 clearSBgDataStructures();
2302 if (itemsToRemove.size() > 0) {
2303 ContentProviderClient client = contentResolver.acquireContentProviderClient(
2305 // Remove dead items
2306 for (long id : itemsToRemove) {
2307 if (DEBUG_LOADERS) {
2308 Log.d(TAG, "Removed id = " + id);
2310 // Don't notify content observers
2312 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
2314 } catch (RemoteException e) {
2315 Log.w(TAG, "Could not remove id = " + id);
2320 if (restoredRows.size() > 0) {
2321 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
2323 // Update restored items that no longer require special handling
2325 StringBuilder selectionBuilder = new StringBuilder();
2326 selectionBuilder.append(LauncherSettings.Favorites._ID);
2327 selectionBuilder.append(" IN (");
2328 selectionBuilder.append(TextUtils.join(", ", restoredRows));
2329 selectionBuilder.append(")");
2330 ContentValues values = new ContentValues();
2331 values.put(LauncherSettings.Favorites.RESTORED, 0);
2332 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
2333 values, selectionBuilder.toString(), null);
2334 } catch (RemoteException e) {
2335 Log.w(TAG, "Could not update restored rows");
2339 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
2340 context.registerReceiver(new AppsAvailabilityCheck(),
2341 new IntentFilter(StartupReceiver.SYSTEM_READY),
2346 long maxScreenId = 0;
2347 // If we're importing we use the old screen order.
2348 for (ItemInfo item: sBgItemsIdMap.values()) {
2349 long screenId = item.screenId;
2350 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2351 !sBgWorkspaceScreens.contains(screenId)) {
2352 sBgWorkspaceScreens.add(screenId);
2353 if (screenId > maxScreenId) {
2354 maxScreenId = screenId;
2358 Collections.sort(sBgWorkspaceScreens);
2360 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2361 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2362 TextUtils.join(", ", sBgWorkspaceScreens), true);
2364 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
2365 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2367 // Update the max item id after we load an old db
2369 // If we're importing we use the old screen order.
2370 for (ItemInfo item: sBgItemsIdMap.values()) {
2371 maxItemId = Math.max(maxItemId, item.id);
2373 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
2375 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2376 for (Integer i : orderedScreens.keySet()) {
2377 sBgWorkspaceScreens.add(orderedScreens.get(i));
2380 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2381 TextUtils.join(", ", sBgWorkspaceScreens), true);
2383 // Remove any empty screens
2384 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
2385 for (ItemInfo item: sBgItemsIdMap.values()) {
2386 long screenId = item.screenId;
2387 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2388 unusedScreens.contains(screenId)) {
2389 unusedScreens.remove(screenId);
2393 // If there are any empty screens remove them, and update.
2394 if (unusedScreens.size() != 0) {
2396 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
2397 TextUtils.join(", ", unusedScreens), true);
2399 sBgWorkspaceScreens.removeAll(unusedScreens);
2400 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2404 if (DEBUG_LOADERS) {
2405 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2406 Log.d(TAG, "workspace layout: ");
2407 int nScreens = occupied.size();
2408 for (int y = 0; y < countY; y++) {
2411 Iterator<Long> iter = occupied.keySet().iterator();
2412 while (iter.hasNext()) {
2413 long screenId = iter.next();
2417 for (int x = 0; x < countX; x++) {
2418 ItemInfo[][] screen = occupied.get(screenId);
2419 if (x < screen.length && y < screen[x].length) {
2420 line += (screen[x][y] != null) ? "#" : ".";
2426 Log.d(TAG, "[ " + line + " ]");
2433 /** Filters the set of items who are directly or indirectly (via another container) on the
2434 * specified screen. */
2435 private void filterCurrentWorkspaceItems(long currentScreenId,
2436 ArrayList<ItemInfo> allWorkspaceItems,
2437 ArrayList<ItemInfo> currentScreenItems,
2438 ArrayList<ItemInfo> otherScreenItems) {
2439 // Purge any null ItemInfos
2440 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2441 while (iter.hasNext()) {
2442 ItemInfo i = iter.next();
2448 // Order the set of items by their containers first, this allows use to walk through the
2449 // list sequentially, build up a list of containers that are in the specified screen,
2450 // as well as all items in those containers.
2451 Set<Long> itemsOnScreen = new HashSet<Long>();
2452 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2454 public int compare(ItemInfo lhs, ItemInfo rhs) {
2455 return (int) (lhs.container - rhs.container);
2458 for (ItemInfo info : allWorkspaceItems) {
2459 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2460 if (info.screenId == currentScreenId) {
2461 currentScreenItems.add(info);
2462 itemsOnScreen.add(info.id);
2464 otherScreenItems.add(info);
2466 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2467 currentScreenItems.add(info);
2468 itemsOnScreen.add(info.id);
2470 if (itemsOnScreen.contains(info.container)) {
2471 currentScreenItems.add(info);
2472 itemsOnScreen.add(info.id);
2474 otherScreenItems.add(info);
2480 /** Filters the set of widgets which are on the specified screen. */
2481 private void filterCurrentAppWidgets(long currentScreenId,
2482 ArrayList<LauncherAppWidgetInfo> appWidgets,
2483 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2484 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
2486 for (LauncherAppWidgetInfo widget : appWidgets) {
2487 if (widget == null) continue;
2488 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2489 widget.screenId == currentScreenId) {
2490 currentScreenWidgets.add(widget);
2492 otherScreenWidgets.add(widget);
2497 /** Filters the set of folders which are on the specified screen. */
2498 private void filterCurrentFolders(long currentScreenId,
2499 HashMap<Long, ItemInfo> itemsIdMap,
2500 HashMap<Long, FolderInfo> folders,
2501 HashMap<Long, FolderInfo> currentScreenFolders,
2502 HashMap<Long, FolderInfo> otherScreenFolders) {
2504 for (long id : folders.keySet()) {
2505 ItemInfo info = itemsIdMap.get(id);
2506 FolderInfo folder = folders.get(id);
2507 if (info == null || folder == null) continue;
2508 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2509 info.screenId == currentScreenId) {
2510 currentScreenFolders.put(id, folder);
2512 otherScreenFolders.put(id, folder);
2517 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2519 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
2520 final LauncherAppState app = LauncherAppState.getInstance();
2521 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2523 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
2525 public int compare(ItemInfo lhs, ItemInfo rhs) {
2526 int cellCountX = (int) grid.numColumns;
2527 int cellCountY = (int) grid.numRows;
2528 int screenOffset = cellCountX * cellCountY;
2529 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
2530 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
2531 lhs.cellY * cellCountX + lhs.cellX);
2532 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
2533 rhs.cellY * cellCountX + rhs.cellX);
2534 return (int) (lr - rr);
2539 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2540 final ArrayList<Long> orderedScreens) {
2541 final Runnable r = new Runnable() {
2544 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2545 if (callbacks != null) {
2546 callbacks.bindScreens(orderedScreens);
2550 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2553 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2554 final ArrayList<ItemInfo> workspaceItems,
2555 final ArrayList<LauncherAppWidgetInfo> appWidgets,
2556 final HashMap<Long, FolderInfo> folders,
2557 ArrayList<Runnable> deferredBindRunnables) {
2559 final boolean postOnMainThread = (deferredBindRunnables != null);
2561 // Bind the workspace items
2562 int N = workspaceItems.size();
2563 for (int i = 0; i < N; i += ITEMS_CHUNK) {
2564 final int start = i;
2565 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
2566 final Runnable r = new Runnable() {
2569 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2570 if (callbacks != null) {
2571 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2576 if (postOnMainThread) {
2577 synchronized (deferredBindRunnables) {
2578 deferredBindRunnables.add(r);
2581 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2586 if (!folders.isEmpty()) {
2587 final Runnable r = new Runnable() {
2589 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2590 if (callbacks != null) {
2591 callbacks.bindFolders(folders);
2595 if (postOnMainThread) {
2596 synchronized (deferredBindRunnables) {
2597 deferredBindRunnables.add(r);
2600 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2604 // Bind the widgets, one at a time
2605 N = appWidgets.size();
2606 for (int i = 0; i < N; i++) {
2607 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2608 final Runnable r = new Runnable() {
2610 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2611 if (callbacks != null) {
2612 callbacks.bindAppWidget(widget);
2616 if (postOnMainThread) {
2617 deferredBindRunnables.add(r);
2619 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2625 * Binds all loaded data to actual views on the main thread.
2627 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
2628 final long t = SystemClock.uptimeMillis();
2631 // Don't use these two variables in any of the callback runnables.
2632 // Otherwise we hold a reference to them.
2633 final Callbacks oldCallbacks = mCallbacks.get();
2634 if (oldCallbacks == null) {
2635 // This launcher has exited and nobody bothered to tell us. Just bail.
2636 Log.w(TAG, "LoaderTask running with no launcher");
2640 // Save a copy of all the bg-thread collections
2641 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2642 ArrayList<LauncherAppWidgetInfo> appWidgets =
2643 new ArrayList<LauncherAppWidgetInfo>();
2644 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2645 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
2646 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
2647 synchronized (sBgLock) {
2648 workspaceItems.addAll(sBgWorkspaceItems);
2649 appWidgets.addAll(sBgAppWidgets);
2650 folders.putAll(sBgFolders);
2651 itemsIdMap.putAll(sBgItemsIdMap);
2652 orderedScreenIds.addAll(sBgWorkspaceScreens);
2655 final boolean isLoadingSynchronously =
2656 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
2657 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
2658 oldCallbacks.getCurrentWorkspaceScreen();
2659 if (currScreen >= orderedScreenIds.size()) {
2660 // There may be no workspace screens (just hotseat items and an empty page).
2661 currScreen = PagedView.INVALID_RESTORE_PAGE;
2663 final int currentScreen = currScreen;
2664 final long currentScreenId = currentScreen < 0
2665 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
2667 // Load all the items that are on the current page first (and in the process, unbind
2668 // all the existing workspace items before we call startBinding() below.
2669 unbindWorkspaceItemsOnMainThread();
2671 // Separate the items that are on the current screen, and all the other remaining items
2672 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2673 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2674 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
2675 new ArrayList<LauncherAppWidgetInfo>();
2676 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
2677 new ArrayList<LauncherAppWidgetInfo>();
2678 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2679 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2681 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
2682 otherWorkspaceItems);
2683 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
2685 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
2687 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2688 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2690 // Tell the workspace that we're about to start binding items
2691 r = new Runnable() {
2693 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2694 if (callbacks != null) {
2695 callbacks.startBinding();
2699 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2701 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2703 // Load items on the current page
2704 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
2705 currentFolders, null);
2706 if (isLoadingSynchronously) {
2707 r = new Runnable() {
2709 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2710 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
2711 callbacks.onPageBoundSynchronously(currentScreen);
2715 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2718 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2719 // work until after the first render)
2720 synchronized (mDeferredBindRunnables) {
2721 mDeferredBindRunnables.clear();
2723 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
2724 (isLoadingSynchronously ? mDeferredBindRunnables : null));
2726 // Tell the workspace that we're done binding items
2727 r = new Runnable() {
2729 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2730 if (callbacks != null) {
2731 callbacks.finishBindingItems(isUpgradePath);
2734 // If we're profiling, ensure this is the last thing in the queue.
2735 if (DEBUG_LOADERS) {
2736 Log.d(TAG, "bound workspace in "
2737 + (SystemClock.uptimeMillis()-t) + "ms");
2740 mIsLoadingAndBindingWorkspace = false;
2743 if (isLoadingSynchronously) {
2744 synchronized (mDeferredBindRunnables) {
2745 mDeferredBindRunnables.add(r);
2748 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2752 private void loadAndBindAllApps() {
2753 if (DEBUG_LOADERS) {
2754 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2756 if (!mAllAppsLoaded) {
2758 synchronized (LoaderTask.this) {
2762 mAllAppsLoaded = true;
2769 private void onlyBindAllApps() {
2770 final Callbacks oldCallbacks = mCallbacks.get();
2771 if (oldCallbacks == null) {
2772 // This launcher has exited and nobody bothered to tell us. Just bail.
2773 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2778 @SuppressWarnings("unchecked")
2779 final ArrayList<AppInfo> list
2780 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
2781 Runnable r = new Runnable() {
2783 final long t = SystemClock.uptimeMillis();
2784 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2785 if (callbacks != null) {
2786 callbacks.bindAllApplications(list);
2788 if (DEBUG_LOADERS) {
2789 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
2790 + (SystemClock.uptimeMillis()-t) + "ms");
2794 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
2795 if (isRunningOnMainThread) {
2802 private void loadAllApps() {
2803 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2805 final Callbacks oldCallbacks = mCallbacks.get();
2806 if (oldCallbacks == null) {
2807 // This launcher has exited and nobody bothered to tell us. Just bail.
2808 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
2812 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2813 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2815 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2817 // Clear the list of apps
2818 mBgAllAppsList.clear();
2819 SharedPreferences prefs = mContext.getSharedPreferences(
2820 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
2821 for (UserHandleCompat user : profiles) {
2822 // Query for the set of apps
2823 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2824 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
2825 if (DEBUG_LOADERS) {
2826 Log.d(TAG, "getActivityList took "
2827 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
2828 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
2830 // Fail if we don't have any apps
2831 // TODO: Fix this. Only fail for the current user.
2832 if (apps == null || apps.isEmpty()) {
2835 // Sort the applications by name
2836 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2837 Collections.sort(apps,
2838 new LauncherModel.ShortcutNameComparator(mLabelCache));
2839 if (DEBUG_LOADERS) {
2840 Log.d(TAG, "sort took "
2841 + (SystemClock.uptimeMillis()-sortTime) + "ms");
2844 // Create the ApplicationInfos
2845 for (int i = 0; i < apps.size(); i++) {
2846 LauncherActivityInfoCompat app = apps.get(i);
2847 // This builds the icon bitmaps.
2848 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
2851 if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) {
2852 // Add shortcuts for packages which were installed while launcher was dead.
2853 String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
2854 + mUserManager.getSerialNumberForUser(user);
2855 Set<String> packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET);
2856 HashSet<String> newPackageSet = new HashSet<String>();
2858 for (LauncherActivityInfoCompat info : apps) {
2859 String packageName = info.getComponentName().getPackageName();
2860 if (!packagesAdded.contains(packageName)
2861 && !newPackageSet.contains(packageName)) {
2862 InstallShortcutReceiver.queueInstallShortcut(info, mContext);
2864 newPackageSet.add(packageName);
2867 prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit();
2870 // Huh? Shouldn't this be inside the Runnable below?
2871 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2872 mBgAllAppsList.added = new ArrayList<AppInfo>();
2874 // Post callback on main thread
2875 mHandler.post(new Runnable() {
2877 final long bindTime = SystemClock.uptimeMillis();
2878 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2879 if (callbacks != null) {
2880 callbacks.bindAllApplications(added);
2881 if (DEBUG_LOADERS) {
2882 Log.d(TAG, "bound " + added.size() + " apps in "
2883 + (SystemClock.uptimeMillis() - bindTime) + "ms");
2886 Log.i(TAG, "not binding apps: no Launcher activity");
2891 if (DEBUG_LOADERS) {
2892 Log.d(TAG, "Icons processed in "
2893 + (SystemClock.uptimeMillis() - loadTime) + "ms");
2897 public void dumpState() {
2898 synchronized (sBgLock) {
2899 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2900 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2901 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2902 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2903 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2908 void enqueuePackageUpdated(PackageUpdatedTask task) {
2912 private class AppsAvailabilityCheck extends BroadcastReceiver {
2915 public void onReceive(Context context, Intent intent) {
2916 synchronized (sBgLock) {
2917 final LauncherAppsCompat launcherApps = LauncherAppsCompat
2918 .getInstance(mApp.getContext());
2919 final PackageManager manager = context.getPackageManager();
2920 final ArrayList<String> packagesRemoved = new ArrayList<String>();
2921 final ArrayList<String> packagesUnavailable = new ArrayList<String>();
2922 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2923 UserHandleCompat user = entry.getKey();
2924 packagesRemoved.clear();
2925 packagesUnavailable.clear();
2926 for (String pkg : entry.getValue()) {
2927 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
2928 boolean packageOnSdcard = launcherApps.isAppEnabled(
2929 manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
2930 if (packageOnSdcard) {
2931 Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
2932 packagesUnavailable.add(pkg);
2934 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
2935 packagesRemoved.add(pkg);
2939 if (!packagesRemoved.isEmpty()) {
2940 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
2941 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
2943 if (!packagesUnavailable.isEmpty()) {
2944 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
2945 packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
2948 sPendingPackages.clear();
2953 private class PackageUpdatedTask implements Runnable {
2956 UserHandleCompat mUser;
2958 public static final int OP_NONE = 0;
2959 public static final int OP_ADD = 1;
2960 public static final int OP_UPDATE = 2;
2961 public static final int OP_REMOVE = 3; // uninstlled
2962 public static final int OP_UNAVAILABLE = 4; // external media unmounted
2965 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
2967 mPackages = packages;
2972 final Context context = mApp.getContext();
2974 final String[] packages = mPackages;
2975 final int N = packages.length;
2978 for (int i=0; i<N; i++) {
2979 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
2980 mIconCache.remove(packages[i], mUser);
2981 mBgAllAppsList.addPackage(context, packages[i], mUser);
2984 // Auto add shortcuts for added packages.
2985 if (ADD_MANAGED_PROFILE_SHORTCUTS
2986 && !UserHandleCompat.myUserHandle().equals(mUser)) {
2987 SharedPreferences prefs = context.getSharedPreferences(
2988 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
2989 String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
2990 + mUserManager.getSerialNumberForUser(mUser);
2991 Set<String> shortcutSet = new HashSet<String>(
2992 prefs.getStringSet(shortcutsSetKey,Collections.EMPTY_SET));
2994 for (int i=0; i<N; i++) {
2995 if (!shortcutSet.contains(packages[i])) {
2996 shortcutSet.add(packages[i]);
2997 List<LauncherActivityInfoCompat> activities =
2998 mLauncherApps.getActivityList(packages[i], mUser);
2999 if (activities != null && !activities.isEmpty()) {
3000 InstallShortcutReceiver.queueInstallShortcut(
3001 activities.get(0), context);
3006 prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
3010 for (int i=0; i<N; i++) {
3011 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
3012 mBgAllAppsList.updatePackage(context, packages[i], mUser);
3013 WidgetPreviewLoader.removePackageFromDb(
3014 mApp.getWidgetPreviewCacheDb(), packages[i]);
3018 // Remove the packageName for the set of auto-installed shortcuts. This
3019 // will ensure that the shortcut when the app is installed again.
3020 if (ADD_MANAGED_PROFILE_SHORTCUTS
3021 && !UserHandleCompat.myUserHandle().equals(mUser)) {
3022 SharedPreferences prefs = context.getSharedPreferences(
3023 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
3024 String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX
3025 + mUserManager.getSerialNumberForUser(mUser);
3026 HashSet<String> shortcutSet = new HashSet<String>(
3027 prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET));
3028 shortcutSet.removeAll(Arrays.asList(mPackages));
3029 prefs.edit().putStringSet(shortcutsSetKey, shortcutSet).commit();
3032 case OP_UNAVAILABLE:
3033 boolean clearCache = mOp == OP_REMOVE;
3034 for (int i=0; i<N; i++) {
3035 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
3036 mBgAllAppsList.removePackage(packages[i], mUser, clearCache);
3037 WidgetPreviewLoader.removePackageFromDb(
3038 mApp.getWidgetPreviewCacheDb(), packages[i]);
3043 ArrayList<AppInfo> added = null;
3044 ArrayList<AppInfo> modified = null;
3045 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
3047 if (mBgAllAppsList.added.size() > 0) {
3048 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
3049 mBgAllAppsList.added.clear();
3051 if (mBgAllAppsList.modified.size() > 0) {
3052 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
3053 mBgAllAppsList.modified.clear();
3055 if (mBgAllAppsList.removed.size() > 0) {
3056 removedApps.addAll(mBgAllAppsList.removed);
3057 mBgAllAppsList.removed.clear();
3060 final Callbacks callbacks = getCallback();
3061 if (callbacks == null) {
3062 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
3066 final HashMap<ComponentName, AppInfo> addedOrUpdatedApps =
3067 new HashMap<ComponentName, AppInfo>();
3069 if (added != null) {
3070 // Ensure that we add all the workspace applications to the db
3071 if (LauncherAppState.isDisableAllApps()) {
3072 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
3073 addAndBindAddedWorkspaceApps(context, addedInfos);
3075 addAppsToAllApps(context, added);
3077 for (AppInfo ai : added) {
3078 addedOrUpdatedApps.put(ai.componentName, ai);
3082 if (modified != null) {
3083 final ArrayList<AppInfo> modifiedFinal = modified;
3084 for (AppInfo ai : modified) {
3085 addedOrUpdatedApps.put(ai.componentName, ai);
3088 mHandler.post(new Runnable() {
3090 Callbacks cb = getCallback();
3091 if (callbacks == cb && cb != null) {
3092 callbacks.bindAppsUpdated(modifiedFinal);
3098 // Update shortcut infos
3099 if (mOp == OP_ADD || mOp == OP_UPDATE) {
3100 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
3101 final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
3102 final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
3104 HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
3105 synchronized (sBgLock) {
3106 for (ItemInfo info : sBgItemsIdMap.values()) {
3107 if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
3108 ShortcutInfo si = (ShortcutInfo) info;
3109 boolean infoUpdated = false;
3110 boolean shortcutUpdated = false;
3112 // Update shortcuts which use iconResource.
3113 if ((si.iconResource != null)
3114 && packageSet.contains(si.iconResource.packageName)) {
3115 Bitmap icon = Utilities.createIconBitmap(si.iconResource.packageName,
3116 si.iconResource.resourceName, mIconCache, context);
3119 si.usingFallbackIcon = false;
3124 ComponentName cn = si.getTargetComponent();
3125 if (cn != null && packageSet.contains(cn.getPackageName())) {
3126 AppInfo appInfo = addedOrUpdatedApps.get(cn);
3128 if (si.isPromise()) {
3129 mIconCache.deletePreloadedIcon(cn, mUser);
3130 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
3131 // Auto install icon
3132 PackageManager pm = context.getPackageManager();
3133 ResolveInfo matched = pm.resolveActivity(
3134 new Intent(Intent.ACTION_MAIN)
3135 .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
3136 PackageManager.MATCH_DEFAULT_ONLY);
3137 if (matched == null) {
3138 // Try to find the best match activity.
3139 Intent intent = pm.getLaunchIntentForPackage(
3140 cn.getPackageName());
3141 if (intent != null) {
3142 cn = intent.getComponent();
3143 appInfo = addedOrUpdatedApps.get(cn);
3146 if ((intent == null) || (appInfo == null)) {
3147 removedShortcuts.add(si);
3150 si.promisedIntent = intent;
3154 // Restore the shortcut.
3155 si.intent = si.promisedIntent;
3156 si.promisedIntent = null;
3157 si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON
3158 & ~ShortcutInfo.FLAG_AUTOINTALL_ICON
3159 & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
3162 si.updateIcon(mIconCache);
3165 if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
3166 && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
3167 si.updateIcon(mIconCache);
3168 si.title = appInfo.title.toString();
3169 si.contentDescription = appInfo.contentDescription;
3173 if ((si.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
3174 // Since package was just updated, the target must be available now.
3175 si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
3176 shortcutUpdated = true;
3180 if (infoUpdated || shortcutUpdated) {
3181 updatedShortcuts.add(si);
3184 updateItemInDatabase(context, si);
3186 } else if (info instanceof LauncherAppWidgetInfo) {
3187 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
3188 if (mUser.equals(widgetInfo.user)
3189 && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
3190 && packageSet.contains(widgetInfo.providerName.getPackageName())) {
3191 widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
3192 widgets.add(widgetInfo);
3193 updateItemInDatabase(context, widgetInfo);
3199 if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
3200 mHandler.post(new Runnable() {
3203 Callbacks cb = getCallback();
3204 if (callbacks == cb && cb != null) {
3205 callbacks.bindShortcutsChanged(
3206 updatedShortcuts, removedShortcuts, mUser);
3210 if (!removedShortcuts.isEmpty()) {
3211 deleteItemsFromDatabase(context, removedShortcuts);
3214 if (!widgets.isEmpty()) {
3215 mHandler.post(new Runnable() {
3217 Callbacks cb = getCallback();
3218 if (callbacks == cb && cb != null) {
3219 callbacks.bindWidgetsRestored(widgets);
3226 final ArrayList<String> removedPackageNames =
3227 new ArrayList<String>();
3228 if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
3229 // Mark all packages in the broadcast to be removed
3230 removedPackageNames.addAll(Arrays.asList(packages));
3231 } else if (mOp == OP_UPDATE) {
3232 // Mark disabled packages in the broadcast to be removed
3233 for (int i=0; i<N; i++) {
3234 if (isPackageDisabled(context, packages[i], mUser)) {
3235 removedPackageNames.add(packages[i]);
3240 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
3241 final int removeReason;
3242 if (mOp == OP_UNAVAILABLE) {
3243 removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
3245 // Remove all the components associated with this package
3246 for (String pn : removedPackageNames) {
3247 deletePackageFromDatabase(context, pn, mUser);
3249 // Remove all the specific components
3250 for (AppInfo a : removedApps) {
3251 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
3252 deleteItemsFromDatabase(context, infos);
3257 // Remove any queued items from the install queue
3258 InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
3259 // Call the components-removed callback
3260 mHandler.post(new Runnable() {
3262 Callbacks cb = getCallback();
3263 if (callbacks == cb && cb != null) {
3264 callbacks.bindComponentsRemoved(
3265 removedPackageNames, removedApps, mUser, removeReason);
3271 final ArrayList<Object> widgetsAndShortcuts =
3272 getSortedWidgetsAndShortcuts(context);
3273 mHandler.post(new Runnable() {
3276 Callbacks cb = getCallback();
3277 if (callbacks == cb && cb != null) {
3278 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
3283 // Write all the logs to disk
3284 mHandler.post(new Runnable() {
3286 Callbacks cb = getCallback();
3287 if (callbacks == cb && cb != null) {
3288 callbacks.dumpLogsToLocalData();
3295 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
3296 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
3297 PackageManager packageManager = context.getPackageManager();
3298 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
3299 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
3301 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
3302 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
3303 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
3304 return widgetsAndShortcuts;
3307 private static boolean isPackageDisabled(Context context, String packageName,
3308 UserHandleCompat user) {
3309 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3310 return !launcherApps.isPackageEnabledForProfile(packageName, user);
3313 public static boolean isValidPackageActivity(Context context, ComponentName cn,
3314 UserHandleCompat user) {
3318 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3319 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
3322 return launcherApps.isActivityEnabledForProfile(cn, user);
3325 public static boolean isValidPackage(Context context, String packageName,
3326 UserHandleCompat user) {
3327 if (packageName == null) {
3330 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3331 return launcherApps.isPackageEnabledForProfile(packageName, user);
3335 * Make an ShortcutInfo object for a restored application or shortcut item that points
3336 * to a package that is not yet installed on the system.
3338 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
3340 final ShortcutInfo info = new ShortcutInfo();
3341 info.user = UserHandleCompat.myUserHandle();
3342 mIconCache.getTitleAndIcon(info, intent, info.user, true);
3344 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
3345 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
3346 if (!TextUtils.isEmpty(title)) {
3349 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
3350 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
3351 if (TextUtils.isEmpty(info.title)) {
3352 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
3354 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
3356 throw new InvalidParameterException("Invalid restoreType " + promiseType);
3359 info.contentDescription = mUserManager.getBadgedLabelForUser(
3360 info.title.toString(), info.user);
3361 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3362 info.promisedIntent = intent;
3367 * Make an Intent object for a restored application or shortcut item that points
3368 * to the market page for the item.
3370 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
3371 ComponentName componentName = intent.getComponent();
3372 return getMarketIntent(componentName.getPackageName());
3375 static Intent getMarketIntent(String packageName) {
3376 return new Intent(Intent.ACTION_VIEW)
3377 .setData(new Uri.Builder()
3379 .authority("details")
3380 .appendQueryParameter("id", packageName)
3385 * This is called from the code that adds shortcuts from the intent receiver. This
3386 * doesn't have a Cursor, but
3388 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3389 UserHandleCompat user, Context context) {
3390 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
3394 * Make an ShortcutInfo object for a shortcut that is an application.
3396 * If c is not null, then it will be used to fill in missing data like the title and icon.
3398 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3399 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
3400 HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
3402 Log.d(TAG, "Null user found in getShortcutInfo");
3406 ComponentName componentName = intent.getComponent();
3407 if (componentName == null) {
3408 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
3412 Intent newIntent = new Intent(intent.getAction(), null);
3413 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3414 newIntent.setComponent(componentName);
3415 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
3416 if ((lai == null) && !allowMissingTarget) {
3417 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
3421 final ShortcutInfo info = new ShortcutInfo();
3423 // the resource -- This may implicitly give us back the fallback icon,
3424 // but don't worry about that. All we're doing with usingFallbackIcon is
3425 // to avoid saving lots of copies of that in the database, and most apps
3426 // have icons anyway.
3427 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
3432 icon = getIconFromCursor(c, iconIndex, context);
3435 // the fallback icon
3437 icon = mIconCache.getDefaultIcon(user);
3438 info.usingFallbackIcon = true;
3443 if (labelCache != null) {
3444 info.title = labelCache.get(componentName);
3447 // from the resource
3448 if (info.title == null && lai != null) {
3449 info.title = lai.getLabel();
3450 if (labelCache != null) {
3451 labelCache.put(componentName, info.title);
3455 if (info.title == null) {
3457 info.title = c.getString(titleIndex);
3460 // fall back to the class name of the activity
3461 if (info.title == null) {
3462 info.title = componentName.getClassName();
3464 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
3466 info.contentDescription = mUserManager.getBadgedLabelForUser(
3467 info.title.toString(), info.user);
3471 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
3473 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
3474 for (ItemInfo i : infos) {
3475 if (i instanceof ShortcutInfo) {
3476 ShortcutInfo info = (ShortcutInfo) i;
3477 ComponentName cn = info.getTargetComponent();
3478 if (cn != null && f.filterItem(null, info, cn)) {
3481 } else if (i instanceof FolderInfo) {
3482 FolderInfo info = (FolderInfo) i;
3483 for (ShortcutInfo s : info.contents) {
3484 ComponentName cn = s.getTargetComponent();
3485 if (cn != null && f.filterItem(info, s, cn)) {
3489 } else if (i instanceof LauncherAppWidgetInfo) {
3490 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
3491 ComponentName cn = info.providerName;
3492 if (cn != null && f.filterItem(null, info, cn)) {
3497 return new ArrayList<ItemInfo>(filtered);
3500 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
3501 final UserHandleCompat user) {
3502 ItemInfoFilter filter = new ItemInfoFilter() {
3504 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
3505 if (info.user == null) {
3506 return cn.equals(cname);
3508 return cn.equals(cname) && info.user.equals(user);
3512 return filterItemInfos(sBgItemsIdMap.values(), filter);
3516 * Make an ShortcutInfo object for a shortcut that isn't an application.
3518 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
3519 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
3523 final ShortcutInfo info = new ShortcutInfo();
3524 // Non-app shortcuts are only supported for current user.
3525 info.user = UserHandleCompat.myUserHandle();
3526 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3528 // TODO: If there's an explicit component and we can't install that, delete it.
3530 info.title = c.getString(titleIndex);
3532 int iconType = c.getInt(iconTypeIndex);
3534 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
3535 String packageName = c.getString(iconPackageIndex);
3536 String resourceName = c.getString(iconResourceIndex);
3537 info.customIcon = false;
3539 icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context);
3542 icon = getIconFromCursor(c, iconIndex, context);
3544 // the fallback icon
3546 icon = mIconCache.getDefaultIcon(info.user);
3547 info.usingFallbackIcon = true;
3550 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
3551 icon = getIconFromCursor(c, iconIndex, context);
3553 icon = mIconCache.getDefaultIcon(info.user);
3554 info.customIcon = false;
3555 info.usingFallbackIcon = true;
3557 info.customIcon = true;
3561 icon = mIconCache.getDefaultIcon(info.user);
3562 info.usingFallbackIcon = true;
3563 info.customIcon = false;
3570 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
3571 @SuppressWarnings("all") // suppress dead code warning
3572 final boolean debug = false;
3574 Log.d(TAG, "getIconFromCursor app="
3575 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
3577 byte[] data = c.getBlob(iconIndex);
3579 return Utilities.createIconBitmap(
3580 BitmapFactory.decodeByteArray(data, 0, data.length), context);
3581 } catch (Exception e) {
3587 * Attempts to find an AppWidgetProviderInfo that matches the given component.
3589 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
3590 ComponentName component) {
3591 List<AppWidgetProviderInfo> widgets =
3592 AppWidgetManager.getInstance(context).getInstalledProviders();
3593 for (AppWidgetProviderInfo info : widgets) {
3594 if (info.provider.equals(component)) {
3601 ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
3602 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3603 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3604 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3606 if (intent == null) {
3607 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3608 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3613 boolean customIcon = false;
3614 ShortcutIconResource iconResource = null;
3616 if (bitmap instanceof Bitmap) {
3617 icon = Utilities.createIconBitmap((Bitmap) bitmap, context);
3620 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3621 if (extra instanceof ShortcutIconResource) {
3622 iconResource = (ShortcutIconResource) extra;
3623 icon = Utilities.createIconBitmap(iconResource.packageName,
3624 iconResource.resourceName, mIconCache, context);
3628 final ShortcutInfo info = new ShortcutInfo();
3630 // Only support intents for current user for now. Intents sent from other
3631 // users wouldn't get here without intent forwarding anyway.
3632 info.user = UserHandleCompat.myUserHandle();
3634 icon = mIconCache.getDefaultIcon(info.user);
3635 info.usingFallbackIcon = true;
3640 info.contentDescription = mUserManager.getBadgedLabelForUser(
3641 info.title.toString(), info.user);
3642 info.intent = intent;
3643 info.customIcon = customIcon;
3644 info.iconResource = iconResource;
3649 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
3651 // If apps can't be on SD, don't even bother.
3652 if (!mAppsCanBeOnRemoveableStorage) {
3655 // If this icon doesn't have a custom icon, check to see
3656 // what's stored in the DB, and if it doesn't match what
3657 // we're going to show, store what we are going to show back
3658 // into the DB. We do this so when we're loading, if the
3659 // package manager can't find an icon (for example because
3660 // the app is on SD) then we can use that instead.
3661 if (!info.customIcon && !info.usingFallbackIcon) {
3662 cache.put(info, c.getBlob(iconIndex));
3667 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3668 boolean needSave = false;
3671 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3672 Bitmap loaded = info.getIcon(mIconCache);
3673 needSave = !saved.sameAs(loaded);
3677 } catch (Exception e) {
3681 Log.d(TAG, "going to save icon bitmap for info=" + info);
3682 // This is slower than is ideal, but this only happens once
3683 // or when the app is updated with a new icon.
3684 updateItemInDatabase(context, info);
3689 * Return an existing FolderInfo object if we have encountered this ID previously,
3690 * or make a new one.
3692 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
3693 // See if a placeholder was created for us already
3694 FolderInfo folderInfo = folders.get(id);
3695 if (folderInfo == null) {
3696 // No placeholder -- create a new instance
3697 folderInfo = new FolderInfo();
3698 folders.put(id, folderInfo);
3703 public static final Comparator<AppInfo> getAppNameComparator() {
3704 final Collator collator = Collator.getInstance();
3705 return new Comparator<AppInfo>() {
3706 public final int compare(AppInfo a, AppInfo b) {
3707 if (a.user.equals(b.user)) {
3708 int result = collator.compare(a.title.toString().trim(),
3709 b.title.toString().trim());
3711 result = a.componentName.compareTo(b.componentName);
3715 // TODO Need to figure out rules for sorting
3716 // profiles, this puts work second.
3717 return a.user.toString().compareTo(b.user.toString());
3722 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
3723 = new Comparator<AppInfo>() {
3724 public final int compare(AppInfo a, AppInfo b) {
3725 if (a.firstInstallTime < b.firstInstallTime) return 1;
3726 if (a.firstInstallTime > b.firstInstallTime) return -1;
3730 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3731 if (info.activityInfo != null) {
3732 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3734 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3737 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
3738 private Collator mCollator;
3739 private HashMap<Object, CharSequence> mLabelCache;
3740 ShortcutNameComparator(PackageManager pm) {
3741 mLabelCache = new HashMap<Object, CharSequence>();
3742 mCollator = Collator.getInstance();
3744 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
3745 mLabelCache = labelCache;
3746 mCollator = Collator.getInstance();
3748 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
3749 String labelA, labelB;
3750 ComponentName keyA = a.getComponentName();
3751 ComponentName keyB = b.getComponentName();
3752 if (mLabelCache.containsKey(keyA)) {
3753 labelA = mLabelCache.get(keyA).toString();
3755 labelA = a.getLabel().toString().trim();
3757 mLabelCache.put(keyA, labelA);
3759 if (mLabelCache.containsKey(keyB)) {
3760 labelB = mLabelCache.get(keyB).toString();
3762 labelB = b.getLabel().toString().trim();
3764 mLabelCache.put(keyB, labelB);
3766 return mCollator.compare(labelA, labelB);
3769 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
3770 private final AppWidgetManagerCompat mManager;
3771 private final PackageManager mPackageManager;
3772 private final HashMap<Object, String> mLabelCache;
3773 private final Collator mCollator;
3775 WidgetAndShortcutNameComparator(Context context) {
3776 mManager = AppWidgetManagerCompat.getInstance(context);
3777 mPackageManager = context.getPackageManager();
3778 mLabelCache = new HashMap<Object, String>();
3779 mCollator = Collator.getInstance();
3781 public final int compare(Object a, Object b) {
3782 String labelA, labelB;
3783 if (mLabelCache.containsKey(a)) {
3784 labelA = mLabelCache.get(a);
3786 labelA = (a instanceof AppWidgetProviderInfo)
3787 ? mManager.loadLabel((AppWidgetProviderInfo) a)
3788 : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
3789 mLabelCache.put(a, labelA);
3791 if (mLabelCache.containsKey(b)) {
3792 labelB = mLabelCache.get(b);
3794 labelB = (b instanceof AppWidgetProviderInfo)
3795 ? mManager.loadLabel((AppWidgetProviderInfo) b)
3796 : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
3797 mLabelCache.put(b, labelB);
3799 return mCollator.compare(labelA, labelB);
3803 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3804 return (provider != null) && (provider.provider != null)
3805 && (provider.provider.getPackageName() != null);
3808 public void dumpState() {
3809 Log.d(TAG, "mCallbacks=" + mCallbacks);
3810 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3811 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3812 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3813 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
3814 if (mLoaderTask != null) {
3815 mLoaderTask.dumpState();
3817 Log.d(TAG, "mLoaderTask=null");
3821 public Callbacks getCallback() {
3822 return mCallbacks != null ? mCallbacks.get() : null;