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.PackageManager;
34 import android.content.pm.ProviderInfo;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.Resources;
38 import android.database.Cursor;
39 import android.graphics.Bitmap;
40 import android.graphics.BitmapFactory;
41 import android.net.Uri;
42 import android.os.Environment;
43 import android.os.Handler;
44 import android.os.HandlerThread;
45 import android.os.Parcelable;
46 import android.os.Process;
47 import android.os.RemoteException;
48 import android.os.SystemClock;
49 import android.provider.BaseColumns;
50 import android.text.TextUtils;
51 import android.util.Log;
52 import android.util.Pair;
54 import com.android.launcher3.compat.AppWidgetManagerCompat;
55 import com.android.launcher3.compat.LauncherActivityInfoCompat;
56 import com.android.launcher3.compat.LauncherAppsCompat;
57 import com.android.launcher3.compat.PackageInstallerCompat;
58 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
59 import com.android.launcher3.compat.UserHandleCompat;
60 import com.android.launcher3.compat.UserManagerCompat;
62 import java.lang.ref.WeakReference;
63 import java.net.URISyntaxException;
64 import java.security.InvalidParameterException;
65 import java.text.Collator;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.Collection;
69 import java.util.Collections;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Iterator;
74 import java.util.List;
75 import java.util.Map.Entry;
77 import java.util.TreeMap;
78 import java.util.concurrent.atomic.AtomicBoolean;
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;
91 static final String TAG = "Launcher.Model";
93 // true = use a "More Apps" folder for non-workspace apps on upgrade
94 // false = strew non-workspace apps across the workspace on upgrade
95 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
96 public static final int LOADER_FLAG_NONE = 0;
97 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
98 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
100 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
101 private static final long INVALID_SCREEN_ID = -1L;
103 private final boolean mAppsCanBeOnRemoveableStorage;
104 private final boolean mOldContentProviderExists;
106 private final LauncherAppState mApp;
107 private final Object mLock = new Object();
108 private DeferredHandler mHandler = new DeferredHandler();
109 private LoaderTask mLoaderTask;
110 private boolean mIsLoaderTaskRunning;
111 private volatile boolean mFlushingWorkerThread;
113 // Specific runnable types that are run on the main thread deferred handler, this allows us to
114 // clear all queued binding runnables when the Launcher activity is destroyed.
115 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
116 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
118 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
120 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
122 sWorkerThread.start();
124 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
126 // We start off with everything not loaded. After that, we assume that
127 // our monitoring of the package manager provides all updates and we never
128 // need to do a requery. These are only ever touched from the loader thread.
129 private boolean mWorkspaceLoaded;
130 private boolean mAllAppsLoaded;
132 // When we are loading pages synchronously, we can't just post the binding of items on the side
133 // pages as this delays the rotation process. Instead, we wait for a callback from the first
134 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
135 // a normal load, we also clear this set of Runnables.
136 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
138 private WeakReference<Callbacks> mCallbacks;
140 // < only access in worker thread >
141 AllAppsList mBgAllAppsList;
143 // The lock that must be acquired before referencing any static bg data structures. Unlike
144 // other locks, this one can generally be held long-term because we never expect any of these
145 // static data structures to be referenced outside of the worker thread except on the first
146 // load after configuration change.
147 static final Object sBgLock = new Object();
149 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
150 // LauncherModel to their ids
151 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
153 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
154 // created by LauncherModel that are directly on the home screen (however, no widgets or
155 // shortcuts within folders).
156 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
158 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
159 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
160 new ArrayList<LauncherAppWidgetInfo>();
162 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
163 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
165 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
166 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
168 // sBgWorkspaceScreens is the ordered set of workspace screens.
169 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
171 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
172 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
173 new HashMap<UserHandleCompat, HashSet<String>>();
175 // </ only access in worker thread >
177 private IconCache mIconCache;
179 protected int mPreviousConfigMcc;
181 private final LauncherAppsCompat mLauncherApps;
182 private final UserManagerCompat mUserManager;
184 public interface Callbacks {
185 public boolean setLoadOnResume();
186 public int getCurrentWorkspaceScreen();
187 public void startBinding();
188 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
189 boolean forceAnimateIcons);
190 public void bindScreens(ArrayList<Long> orderedScreenIds);
191 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
192 public void bindFolders(HashMap<Long,FolderInfo> folders);
193 public void finishBindingItems(boolean upgradePath);
194 public void bindAppWidget(LauncherAppWidgetInfo info);
195 public void bindAllApplications(ArrayList<AppInfo> apps);
196 public void bindAppsAdded(ArrayList<Long> newScreens,
197 ArrayList<ItemInfo> addNotAnimated,
198 ArrayList<ItemInfo> addAnimated,
199 ArrayList<AppInfo> addedApps);
200 public void bindAppsUpdated(ArrayList<AppInfo> apps);
201 public void bindAppsRestored(ArrayList<AppInfo> apps);
202 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
203 public void updatePackageBadge(String packageName);
204 public void bindComponentsRemoved(ArrayList<String> packageNames,
205 ArrayList<AppInfo> appInfos, UserHandleCompat user);
206 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
207 public void bindSearchablesChanged();
208 public boolean isAllAppsButtonRank(int rank);
209 public void onPageBoundSynchronously(int page);
210 public void dumpLogsToLocalData();
213 public interface ItemInfoFilter {
214 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
217 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
218 Context context = app.getContext();
220 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
221 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
222 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
224 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
225 ProviderInfo providerInfo =
226 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
227 ProviderInfo redirectProvider =
228 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
230 Log.d(TAG, "Old launcher provider: " + oldProvider);
231 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
233 if (mOldContentProviderExists) {
234 Log.d(TAG, "Old launcher provider exists.");
236 Log.d(TAG, "Old launcher provider does not exist.");
240 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
241 mIconCache = iconCache;
243 final Resources res = context.getResources();
244 Configuration config = res.getConfiguration();
245 mPreviousConfigMcc = config.mcc;
246 mLauncherApps = LauncherAppsCompat.getInstance(context);
247 mUserManager = UserManagerCompat.getInstance(context);
250 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
251 * posted on the main thread handler. */
252 private void runOnMainThread(Runnable r) {
253 runOnMainThread(r, 0);
255 private void runOnMainThread(Runnable r, int type) {
256 if (sWorkerThread.getThreadId() == Process.myTid()) {
257 // If we are on the worker thread, post onto the main handler
264 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
265 * posted on the worker thread handler. */
266 private static void runOnWorkerThread(Runnable r) {
267 if (sWorkerThread.getThreadId() == Process.myTid()) {
270 // If we are not on the worker thread, then post to the worker handler
275 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
276 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
279 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
281 LauncherAppState app = LauncherAppState.getInstance();
282 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
283 final int xCount = (int) grid.numColumns;
284 final int yCount = (int) grid.numRows;
285 boolean[][] occupied = new boolean[xCount][yCount];
287 int cellX, cellY, spanX, spanY;
288 for (int i = 0; i < items.size(); ++i) {
289 final ItemInfo item = items.get(i);
290 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
291 if (item.screenId == screen) {
296 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
297 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
298 occupied[x][y] = true;
305 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
307 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
309 int firstScreenIndex,
310 ArrayList<Long> workspaceScreens) {
311 // Lock on the app so that we don't try and get the items while apps are being added
312 LauncherAppState app = LauncherAppState.getInstance();
313 LauncherModel model = app.getModel();
314 boolean found = false;
316 if (sWorkerThread.getThreadId() != Process.myTid()) {
317 // Flush the LauncherModel worker thread, so that if we just did another
318 // processInstallShortcut, we give it time for its shortcut to get added to the
319 // database (getItemsInLocalCoordinates reads the database)
320 model.flushWorkerThread();
322 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
324 // Try adding to the workspace screens incrementally, starting at the default or center
325 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
326 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
327 int count = workspaceScreens.size();
328 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
329 int[] tmpCoordinates = new int[2];
330 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
331 workspaceScreens.get(screen))) {
332 // Update the Launcher db
333 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
340 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
341 // Process the updated package state
342 Runnable r = new Runnable() {
344 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
345 if (callbacks != null) {
346 callbacks.updatePackageState(installInfo);
353 public void updatePackageBadge(final String packageName) {
354 // Process the updated package badge
355 Runnable r = new Runnable() {
357 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
358 if (callbacks != null) {
359 callbacks.updatePackageBadge(packageName);
366 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
367 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
369 if (allAppsApps == null) {
370 throw new RuntimeException("allAppsApps must not be null");
372 if (allAppsApps.isEmpty()) {
376 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
377 Iterator<AppInfo> iter = allAppsApps.iterator();
378 while (iter.hasNext()) {
379 ItemInfo a = iter.next();
380 if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
381 restoredAppsFinal.add((AppInfo) a);
385 // Process the newly added applications and add them to the database first
386 Runnable r = new Runnable() {
388 runOnMainThread(new Runnable() {
390 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
391 if (callbacks == cb && cb != null) {
392 if (!restoredAppsFinal.isEmpty()) {
393 for (AppInfo info : restoredAppsFinal) {
394 final Intent intent = info.getIntent();
395 if (intent != null) {
396 mIconCache.deletePreloadedIcon(intent.getComponent(),
400 callbacks.bindAppsUpdated(restoredAppsFinal);
402 callbacks.bindAppsAdded(null, null, null, allAppsApps);
408 runOnWorkerThread(r);
411 public void addAndBindAddedWorkspaceApps(final Context context,
412 final ArrayList<ItemInfo> workspaceApps) {
413 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
415 if (workspaceApps == null) {
416 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
418 if (workspaceApps.isEmpty()) {
421 // Process the newly added applications and add them to the database first
422 Runnable r = new Runnable() {
424 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
425 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
426 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
428 // Get the list of workspace screens. We need to append to this list and
429 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
431 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
432 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
433 for (Integer i : orderedScreens.keySet()) {
434 long screenId = orderedScreens.get(i);
435 workspaceScreens.add(screenId);
438 synchronized(sBgLock) {
439 Iterator<ItemInfo> iter = workspaceApps.iterator();
440 while (iter.hasNext()) {
441 ItemInfo a = iter.next();
442 final String name = a.title.toString();
443 final Intent launchIntent = a.getIntent();
445 // Short-circuit this logic if the icon exists somewhere on the workspace
446 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
447 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
448 if (a instanceof AppInfo &&
449 LauncherModel.appWasPromise(context, launchIntent, a.user)) {
450 restoredAppsFinal.add((AppInfo) a);
455 // Add this icon to the db, creating a new page if necessary. If there
456 // is only the empty page then we just add items to the first page.
457 // Otherwise, we add them to the next pages.
458 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
459 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
460 name, launchIntent, startSearchPageIndex, workspaceScreens);
461 if (coords == null) {
462 LauncherProvider lp = LauncherAppState.getLauncherProvider();
464 // If we can't find a valid position, then just add a new screen.
465 // This takes time so we need to re-queue the add until the new
466 // page is added. Create as many screens as necessary to satisfy
467 // the startSearchPageIndex.
468 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
469 workspaceScreens.size());
470 while (numPagesToAdd > 0) {
471 long screenId = lp.generateNewScreenId();
472 // Save the screen id for binding in the workspace
473 workspaceScreens.add(screenId);
474 addedWorkspaceScreensFinal.add(screenId);
478 // Find the coordinate again
479 coords = LauncherModel.findNextAvailableIconSpace(context,
480 name, launchIntent, startSearchPageIndex, workspaceScreens);
482 if (coords == null) {
483 throw new RuntimeException("Coordinates should not be null");
486 ShortcutInfo shortcutInfo;
487 if (a instanceof ShortcutInfo) {
488 shortcutInfo = (ShortcutInfo) a;
489 } else if (a instanceof AppInfo) {
490 shortcutInfo = ((AppInfo) a).makeShortcut();
492 throw new RuntimeException("Unexpected info type");
495 // Add the shortcut to the db
496 addItemToDatabase(context, shortcutInfo,
497 LauncherSettings.Favorites.CONTAINER_DESKTOP,
498 coords.first, coords.second[0], coords.second[1], false);
499 // Save the ShortcutInfo for binding in the workspace
500 addedShortcutsFinal.add(shortcutInfo);
504 // Update the workspace screens
505 updateWorkspaceScreenOrder(context, workspaceScreens);
507 if (!addedShortcutsFinal.isEmpty()) {
508 runOnMainThread(new Runnable() {
510 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
511 if (callbacks == cb && cb != null) {
512 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
513 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
514 if (!addedShortcutsFinal.isEmpty()) {
515 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
516 long lastScreenId = info.screenId;
517 for (ItemInfo i : addedShortcutsFinal) {
518 if (i.screenId == lastScreenId) {
521 addNotAnimated.add(i);
525 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
526 addNotAnimated, addAnimated, null);
527 if (!restoredAppsFinal.isEmpty()) {
528 callbacks.bindAppsUpdated(restoredAppsFinal);
536 runOnWorkerThread(r);
539 public void unbindItemInfosAndClearQueuedBindRunnables() {
540 if (sWorkerThread.getThreadId() == Process.myTid()) {
541 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
545 // Clear any deferred bind runnables
546 synchronized (mDeferredBindRunnables) {
547 mDeferredBindRunnables.clear();
549 // Remove any queued bind runnables
550 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
551 // Unbind all the workspace items
552 unbindWorkspaceItemsOnMainThread();
555 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
556 void unbindWorkspaceItemsOnMainThread() {
557 // Ensure that we don't use the same workspace items data structure on the main thread
558 // by making a copy of workspace items first.
559 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
560 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
561 synchronized (sBgLock) {
562 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
563 tmpAppWidgets.addAll(sBgAppWidgets);
565 Runnable r = new Runnable() {
568 for (ItemInfo item : tmpWorkspaceItems) {
571 for (ItemInfo item : tmpAppWidgets) {
580 * Adds an item to the DB if it was not created previously, or move it to a new
581 * <container, screen, cellX, cellY>
583 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
584 long screenId, int cellX, int cellY) {
585 if (item.container == ItemInfo.NO_ID) {
587 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
589 // From somewhere else
590 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
594 static void checkItemInfoLocked(
595 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
596 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
597 if (modelItem != null && item != modelItem) {
598 // check all the data is consistent
599 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
600 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
601 ShortcutInfo shortcut = (ShortcutInfo) item;
602 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
603 modelShortcut.intent.filterEquals(shortcut.intent) &&
604 modelShortcut.id == shortcut.id &&
605 modelShortcut.itemType == shortcut.itemType &&
606 modelShortcut.container == shortcut.container &&
607 modelShortcut.screenId == shortcut.screenId &&
608 modelShortcut.cellX == shortcut.cellX &&
609 modelShortcut.cellY == shortcut.cellY &&
610 modelShortcut.spanX == shortcut.spanX &&
611 modelShortcut.spanY == shortcut.spanY &&
612 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
613 (modelShortcut.dropPos != null &&
614 shortcut.dropPos != null &&
615 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
616 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
617 // For all intents and purposes, this is the same object
622 // the modelItem needs to match up perfectly with item if our model is
623 // to be consistent with the database-- for now, just require
624 // modelItem == item or the equality check above
625 String msg = "item: " + ((item != null) ? item.toString() : "null") +
627 ((modelItem != null) ? modelItem.toString() : "null") +
628 "Error: ItemInfo passed to checkItemInfo doesn't match original";
629 RuntimeException e = new RuntimeException(msg);
630 if (stackTrace != null) {
631 e.setStackTrace(stackTrace);
637 static void checkItemInfo(final ItemInfo item) {
638 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
639 final long itemId = item.id;
640 Runnable r = new Runnable() {
642 synchronized (sBgLock) {
643 checkItemInfoLocked(itemId, item, stackTrace);
647 runOnWorkerThread(r);
650 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
651 final ItemInfo item, final String callingFunction) {
652 final long itemId = item.id;
653 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
654 final ContentResolver cr = context.getContentResolver();
656 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
657 Runnable r = new Runnable() {
659 cr.update(uri, values, null, null);
660 updateItemArrays(item, itemId, stackTrace);
663 runOnWorkerThread(r);
666 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
667 final ArrayList<ItemInfo> items, final String callingFunction) {
668 final ContentResolver cr = context.getContentResolver();
670 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
671 Runnable r = new Runnable() {
673 ArrayList<ContentProviderOperation> ops =
674 new ArrayList<ContentProviderOperation>();
675 int count = items.size();
676 for (int i = 0; i < count; i++) {
677 ItemInfo item = items.get(i);
678 final long itemId = item.id;
679 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
680 ContentValues values = valuesList.get(i);
682 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
683 updateItemArrays(item, itemId, stackTrace);
687 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
688 } catch (Exception e) {
693 runOnWorkerThread(r);
696 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
697 // Lock on mBgLock *after* the db operation
698 synchronized (sBgLock) {
699 checkItemInfoLocked(itemId, item, stackTrace);
701 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
702 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
703 // Item is in a folder, make sure this folder exists
704 if (!sBgFolders.containsKey(item.container)) {
705 // An items container is being set to a that of an item which is not in
706 // the list of Folders.
707 String msg = "item: " + item + " container being set to: " +
708 item.container + ", not in the list of folders";
713 // Items are added/removed from the corresponding FolderInfo elsewhere, such
714 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
715 // that are on the desktop, as appropriate
716 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
717 if (modelItem != null &&
718 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
719 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
720 switch (modelItem.itemType) {
721 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
722 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
723 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
724 if (!sBgWorkspaceItems.contains(modelItem)) {
725 sBgWorkspaceItems.add(modelItem);
732 sBgWorkspaceItems.remove(modelItem);
737 public void flushWorkerThread() {
738 mFlushingWorkerThread = true;
739 Runnable waiter = new Runnable() {
741 synchronized (this) {
743 mFlushingWorkerThread = false;
748 synchronized(waiter) {
749 runOnWorkerThread(waiter);
750 if (mLoaderTask != null) {
751 synchronized(mLoaderTask) {
752 mLoaderTask.notify();
755 boolean success = false;
760 } catch (InterruptedException e) {
767 * Move an item in the DB to a new <container, screen, cellX, cellY>
769 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
770 final long screenId, final int cellX, final int cellY) {
771 item.container = container;
775 // We store hotseat items in canonical form which is this orientation invariant position
777 if (context instanceof Launcher && screenId < 0 &&
778 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
779 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
781 item.screenId = screenId;
784 final ContentValues values = new ContentValues();
785 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
786 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
787 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
788 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
790 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
794 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
795 * cellX, cellY have already been updated on the ItemInfos.
797 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
798 final long container, final int screen) {
800 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
801 int count = items.size();
803 for (int i = 0; i < count; i++) {
804 ItemInfo item = items.get(i);
805 item.container = container;
807 // We store hotseat items in canonical form which is this orientation invariant position
809 if (context instanceof Launcher && screen < 0 &&
810 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
811 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
814 item.screenId = screen;
817 final ContentValues values = new ContentValues();
818 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
819 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
820 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
821 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
823 contentValues.add(values);
825 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
829 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
831 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
832 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
833 item.container = container;
839 // We store hotseat items in canonical form which is this orientation invariant position
841 if (context instanceof Launcher && screenId < 0 &&
842 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
843 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
845 item.screenId = screenId;
848 final ContentValues values = new ContentValues();
849 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
850 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
851 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
852 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
853 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
854 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
856 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
860 * Update an item to the database in a specified container.
862 static void updateItemInDatabase(Context context, final ItemInfo item) {
863 final ContentValues values = new ContentValues();
864 item.onAddToDatabase(context, values);
865 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
866 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
870 * Returns true if the shortcuts already exists in the database.
871 * we identify a shortcut by its title and intent.
873 static boolean shortcutExists(Context context, String title, Intent intent) {
874 final ContentResolver cr = context.getContentResolver();
875 final Intent intentWithPkg, intentWithoutPkg;
877 if (intent.getComponent() != null) {
878 // If component is not null, an intent with null package will produce
879 // the same result and should also be a match.
880 if (intent.getPackage() != null) {
881 intentWithPkg = intent;
882 intentWithoutPkg = new Intent(intent).setPackage(null);
884 intentWithPkg = new Intent(intent).setPackage(
885 intent.getComponent().getPackageName());
886 intentWithoutPkg = intent;
889 intentWithPkg = intent;
890 intentWithoutPkg = intent;
892 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
893 new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
894 new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
895 boolean result = false;
897 result = c.moveToFirst();
905 * Returns true if the promise shortcuts with the same package name exists on the workspace.
907 static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
908 final ComponentName component = intent.getComponent();
909 if (component == null) {
912 return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
916 * Returns an ItemInfo array containing all the items in the LauncherModel.
917 * The ItemInfo.id is not set through this function.
919 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
920 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
921 final ContentResolver cr = context.getContentResolver();
922 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
923 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
924 LauncherSettings.Favorites.SCREEN,
925 LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
926 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
927 LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
929 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
930 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
931 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
932 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
933 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
934 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
935 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
936 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
937 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
939 while (c.moveToNext()) {
940 ItemInfo item = new ItemInfo();
941 item.cellX = c.getInt(cellXIndex);
942 item.cellY = c.getInt(cellYIndex);
943 item.spanX = Math.max(1, c.getInt(spanXIndex));
944 item.spanY = Math.max(1, c.getInt(spanYIndex));
945 item.container = c.getInt(containerIndex);
946 item.itemType = c.getInt(itemTypeIndex);
947 item.screenId = c.getInt(screenIndex);
948 long serialNumber = c.getInt(profileIdIndex);
949 item.user = userManager.getUserForSerialNumber(serialNumber);
950 // Skip if user has been deleted.
951 if (item.user != null) {
955 } catch (Exception e) {
965 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
967 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
968 final ContentResolver cr = context.getContentResolver();
969 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
970 "_id=? and (itemType=? or itemType=?)",
971 new String[] { String.valueOf(id),
972 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
975 if (c.moveToFirst()) {
976 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
977 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
978 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
979 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
980 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
981 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
983 FolderInfo folderInfo = null;
984 switch (c.getInt(itemTypeIndex)) {
985 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
986 folderInfo = findOrMakeFolder(folderList, id);
990 folderInfo.title = c.getString(titleIndex);
992 folderInfo.container = c.getInt(containerIndex);
993 folderInfo.screenId = c.getInt(screenIndex);
994 folderInfo.cellX = c.getInt(cellXIndex);
995 folderInfo.cellY = c.getInt(cellYIndex);
1007 * Add an item to the database in a specified container. Sets the container, screen, cellX and
1008 * cellY fields of the item. Also assigns an ID to the item.
1010 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
1011 final long screenId, final int cellX, final int cellY, final boolean notify) {
1012 item.container = container;
1015 // We store hotseat items in canonical form which is this orientation invariant position
1017 if (context instanceof Launcher && screenId < 0 &&
1018 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1019 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
1021 item.screenId = screenId;
1024 final ContentValues values = new ContentValues();
1025 final ContentResolver cr = context.getContentResolver();
1026 item.onAddToDatabase(context, values);
1028 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
1029 values.put(LauncherSettings.Favorites._ID, item.id);
1030 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
1032 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
1033 Runnable r = new Runnable() {
1035 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1036 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
1038 // Lock on mBgLock *after* the db operation
1039 synchronized (sBgLock) {
1040 checkItemInfoLocked(item.id, item, stackTrace);
1041 sBgItemsIdMap.put(item.id, item);
1042 switch (item.itemType) {
1043 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1044 sBgFolders.put(item.id, (FolderInfo) item);
1046 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1047 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1048 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1049 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1050 sBgWorkspaceItems.add(item);
1052 if (!sBgFolders.containsKey(item.container)) {
1053 // Adding an item to a folder that doesn't exist.
1054 String msg = "adding item: " + item + " to a folder that " +
1060 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1061 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
1067 runOnWorkerThread(r);
1071 * Creates a new unique child id, for a given cell span across all layouts.
1073 static int getCellLayoutChildId(
1074 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
1075 return (((int) container & 0xFF) << 24)
1076 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
1079 private static ArrayList<ItemInfo> getItemsByPackageName(
1080 final String pn, final UserHandleCompat user) {
1081 ItemInfoFilter filter = new ItemInfoFilter() {
1083 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1084 return cn.getPackageName().equals(pn) && info.user.equals(user);
1087 return filterItemInfos(sBgItemsIdMap.values(), filter);
1091 * Removes all the items from the database corresponding to the specified package.
1093 static void deletePackageFromDatabase(Context context, final String pn,
1094 final UserHandleCompat user) {
1095 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
1099 * Removes the specified item from the database
1103 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
1104 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1106 deleteItemsFromDatabase(context, items);
1110 * Removes the specified items from the database
1114 static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
1115 final ContentResolver cr = context.getContentResolver();
1117 Runnable r = new Runnable() {
1119 for (ItemInfo item : items) {
1120 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1121 cr.delete(uri, null, null);
1123 // Lock on mBgLock *after* the db operation
1124 synchronized (sBgLock) {
1125 switch (item.itemType) {
1126 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1127 sBgFolders.remove(item.id);
1128 for (ItemInfo info: sBgItemsIdMap.values()) {
1129 if (info.container == item.id) {
1130 // We are deleting a folder which still contains items that
1131 // think they are contained by that folder.
1132 String msg = "deleting a folder (" + item + ") which still " +
1133 "contains items (" + info + ")";
1137 sBgWorkspaceItems.remove(item);
1139 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1140 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1141 sBgWorkspaceItems.remove(item);
1143 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1144 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1147 sBgItemsIdMap.remove(item.id);
1148 sBgDbIconCache.remove(item);
1153 runOnWorkerThread(r);
1157 * Update the order of the workspace screens in the database. The array list contains
1158 * a list of screen ids in the order that they should appear.
1160 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
1162 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1163 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1165 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
1166 final ContentResolver cr = context.getContentResolver();
1167 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1169 // Remove any negative screen ids -- these aren't persisted
1170 Iterator<Long> iter = screensCopy.iterator();
1171 while (iter.hasNext()) {
1172 long id = iter.next();
1178 Runnable r = new Runnable() {
1181 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
1183 ops.add(ContentProviderOperation.newDelete(uri).build());
1184 int count = screensCopy.size();
1185 for (int i = 0; i < count; i++) {
1186 ContentValues v = new ContentValues();
1187 long screenId = screensCopy.get(i);
1188 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1189 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1190 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
1194 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1195 } catch (Exception ex) {
1196 throw new RuntimeException(ex);
1199 synchronized (sBgLock) {
1200 sBgWorkspaceScreens.clear();
1201 sBgWorkspaceScreens.addAll(screensCopy);
1205 runOnWorkerThread(r);
1209 * Remove the contents of the specified folder from the database
1211 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
1212 final ContentResolver cr = context.getContentResolver();
1214 Runnable r = new Runnable() {
1216 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
1217 // Lock on mBgLock *after* the db operation
1218 synchronized (sBgLock) {
1219 sBgItemsIdMap.remove(info.id);
1220 sBgFolders.remove(info.id);
1221 sBgDbIconCache.remove(info);
1222 sBgWorkspaceItems.remove(info);
1225 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1226 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
1227 // Lock on mBgLock *after* the db operation
1228 synchronized (sBgLock) {
1229 for (ItemInfo childInfo : info.contents) {
1230 sBgItemsIdMap.remove(childInfo.id);
1231 sBgDbIconCache.remove(childInfo);
1236 runOnWorkerThread(r);
1240 * Set this as the current Launcher activity object for the loader.
1242 public void initialize(Callbacks callbacks) {
1243 synchronized (mLock) {
1244 mCallbacks = new WeakReference<Callbacks>(callbacks);
1249 public void onPackageChanged(String packageName, UserHandleCompat user) {
1250 int op = PackageUpdatedTask.OP_UPDATE;
1251 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1256 public void onPackageRemoved(String packageName, UserHandleCompat user) {
1257 int op = PackageUpdatedTask.OP_REMOVE;
1258 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1263 public void onPackageAdded(String packageName, UserHandleCompat user) {
1264 int op = PackageUpdatedTask.OP_ADD;
1265 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1270 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
1271 boolean replacing) {
1273 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
1275 if (mAppsCanBeOnRemoveableStorage) {
1276 // Only rebind if we support removable storage. It catches the
1278 // apps on the external sd card need to be reloaded
1279 startLoaderFromBackground();
1282 // If we are replacing then just update the packages in the list
1283 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1284 packageNames, user));
1289 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
1290 boolean replacing) {
1292 enqueuePackageUpdated(new PackageUpdatedTask(
1293 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1300 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1301 * ACTION_PACKAGE_CHANGED.
1304 public void onReceive(Context context, Intent intent) {
1305 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
1307 final String action = intent.getAction();
1308 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
1309 // If we have changed locale we need to clear out the labels in all apps/workspace.
1311 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1312 // Check if configuration change was an mcc/mnc change which would affect app resources
1313 // and we would need to clear out the labels in all apps/workspace. Same handling as
1314 // above for ACTION_LOCALE_CHANGED
1315 Configuration currentConfig = context.getResources().getConfiguration();
1316 if (mPreviousConfigMcc != currentConfig.mcc) {
1317 Log.d(TAG, "Reload apps on config change. curr_mcc:"
1318 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
1321 // Update previousConfig
1322 mPreviousConfigMcc = currentConfig.mcc;
1323 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1324 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
1325 if (mCallbacks != null) {
1326 Callbacks callbacks = mCallbacks.get();
1327 if (callbacks != null) {
1328 callbacks.bindSearchablesChanged();
1334 void forceReload() {
1335 resetLoadedState(true, true);
1337 // Do this here because if the launcher activity is running it will be restarted.
1338 // If it's not running startLoaderFromBackground will merely tell it that it needs
1340 startLoaderFromBackground();
1343 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1344 synchronized (mLock) {
1345 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1346 // mWorkspaceLoaded to true later
1348 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1349 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1354 * When the launcher is in the background, it's possible for it to miss paired
1355 * configuration changes. So whenever we trigger the loader from the background
1356 * tell the launcher that it needs to re-run the loader when it comes back instead
1359 public void startLoaderFromBackground() {
1360 boolean runLoader = false;
1361 if (mCallbacks != null) {
1362 Callbacks callbacks = mCallbacks.get();
1363 if (callbacks != null) {
1364 // Only actually run the loader if they're not paused.
1365 if (!callbacks.setLoadOnResume()) {
1371 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
1375 // If there is already a loader task running, tell it to stop.
1376 // returns true if isLaunching() was true on the old task
1377 private boolean stopLoaderLocked() {
1378 boolean isLaunching = false;
1379 LoaderTask oldTask = mLoaderTask;
1380 if (oldTask != null) {
1381 if (oldTask.isLaunching()) {
1384 oldTask.stopLocked();
1389 public boolean isCurrentCallbacks(Callbacks callbacks) {
1390 return (mCallbacks != null && mCallbacks.get() == callbacks);
1393 public void startLoader(boolean isLaunching, int synchronousBindPage) {
1394 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1397 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
1398 synchronized (mLock) {
1399 if (DEBUG_LOADERS) {
1400 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1403 // Clear any deferred bind-runnables from the synchronized load process
1404 // We must do this before any loading/binding is scheduled below.
1405 synchronized (mDeferredBindRunnables) {
1406 mDeferredBindRunnables.clear();
1409 // Don't bother to start the thread if we know it's not going to do anything
1410 if (mCallbacks != null && mCallbacks.get() != null) {
1411 // If there is already one running, tell it to stop.
1412 // also, don't downgrade isLaunching if we're already running
1413 isLaunching = isLaunching || stopLoaderLocked();
1414 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
1415 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1416 && mAllAppsLoaded && mWorkspaceLoaded) {
1417 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1419 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1420 sWorker.post(mLoaderTask);
1426 void bindRemainingSynchronousPages() {
1427 // Post the remaining side pages to be loaded
1428 if (!mDeferredBindRunnables.isEmpty()) {
1429 Runnable[] deferredBindRunnables = null;
1430 synchronized (mDeferredBindRunnables) {
1431 deferredBindRunnables = mDeferredBindRunnables.toArray(
1432 new Runnable[mDeferredBindRunnables.size()]);
1433 mDeferredBindRunnables.clear();
1435 for (final Runnable r : deferredBindRunnables) {
1436 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
1441 public void stopLoader() {
1442 synchronized (mLock) {
1443 if (mLoaderTask != null) {
1444 mLoaderTask.stopLocked();
1449 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1450 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1451 final ContentResolver contentResolver = context.getContentResolver();
1452 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1453 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1454 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1457 final int idIndex = sc.getColumnIndexOrThrow(
1458 LauncherSettings.WorkspaceScreens._ID);
1459 final int rankIndex = sc.getColumnIndexOrThrow(
1460 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1461 while (sc.moveToNext()) {
1463 long screenId = sc.getLong(idIndex);
1464 int rank = sc.getInt(rankIndex);
1465 orderedScreens.put(rank, screenId);
1466 } catch (Exception e) {
1467 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true);
1475 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1476 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1477 for (Integer i : orderedScreens.keySet()) {
1478 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1480 Launcher.addDumpLog(TAG, "11683562 - screens: " +
1481 TextUtils.join(", ", orderedScreensPairs), true);
1482 return orderedScreens;
1485 public boolean isAllAppsLoaded() {
1486 return mAllAppsLoaded;
1489 boolean isLoadingWorkspace() {
1490 synchronized (mLock) {
1491 if (mLoaderTask != null) {
1492 return mLoaderTask.isLoadingWorkspace();
1499 * Runnable for the thread that loads the contents of the launcher:
1504 private class LoaderTask implements Runnable {
1505 private Context mContext;
1506 private boolean mIsLaunching;
1507 private boolean mIsLoadingAndBindingWorkspace;
1508 private boolean mStopped;
1509 private boolean mLoadAndBindStepFinished;
1512 private HashMap<Object, CharSequence> mLabelCache;
1514 LoaderTask(Context context, boolean isLaunching, int flags) {
1516 mIsLaunching = isLaunching;
1517 mLabelCache = new HashMap<Object, CharSequence>();
1521 boolean isLaunching() {
1522 return mIsLaunching;
1525 boolean isLoadingWorkspace() {
1526 return mIsLoadingAndBindingWorkspace;
1529 /** Returns whether this is an upgrade path */
1530 private boolean loadAndBindWorkspace() {
1531 mIsLoadingAndBindingWorkspace = true;
1533 // Load the workspace
1534 if (DEBUG_LOADERS) {
1535 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
1538 boolean isUpgradePath = false;
1539 if (!mWorkspaceLoaded) {
1540 isUpgradePath = loadWorkspace();
1541 synchronized (LoaderTask.this) {
1543 return isUpgradePath;
1545 mWorkspaceLoaded = true;
1549 // Bind the workspace
1550 bindWorkspace(-1, isUpgradePath);
1551 return isUpgradePath;
1554 private void waitForIdle() {
1555 // Wait until the either we're stopped or the other threads are done.
1556 // This way we don't start loading all apps until the workspace has settled
1558 synchronized (LoaderTask.this) {
1559 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1561 mHandler.postIdle(new Runnable() {
1563 synchronized (LoaderTask.this) {
1564 mLoadAndBindStepFinished = true;
1565 if (DEBUG_LOADERS) {
1566 Log.d(TAG, "done with previous binding step");
1568 LoaderTask.this.notify();
1573 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
1575 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1576 // wait no longer than 1sec at a time
1578 } catch (InterruptedException ex) {
1582 if (DEBUG_LOADERS) {
1583 Log.d(TAG, "waited "
1584 + (SystemClock.uptimeMillis()-workspaceWaitTime)
1585 + "ms for previous step to finish binding");
1590 void runBindSynchronousPage(int synchronousBindPage) {
1591 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
1592 // Ensure that we have a valid page index to load synchronously
1593 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1594 "valid page index");
1596 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1597 // Ensure that we don't try and bind a specified page when the pages have not been
1598 // loaded already (we should load everything asynchronously in that case)
1599 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1601 synchronized (mLock) {
1602 if (mIsLoaderTaskRunning) {
1603 // Ensure that we are never running the background loading at this point since
1604 // we also touch the background collections
1605 throw new RuntimeException("Error! Background loading is already running");
1609 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1610 // data structures, we can't allow any other thread to touch that data, but because
1611 // this call is synchronous, we can get away with not locking).
1613 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
1614 // operations from the previous activity. We need to ensure that all queued operations
1615 // are executed before any synchronous binding work is done.
1618 // Divide the set of loaded items into those that we are binding synchronously, and
1619 // everything else that is to be bound normally (asynchronously).
1620 bindWorkspace(synchronousBindPage, false);
1621 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1627 boolean isUpgrade = false;
1629 synchronized (mLock) {
1630 mIsLoaderTaskRunning = true;
1632 // Optimize for end-user experience: if the Launcher is up and // running with the
1633 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1634 // workspace first (default).
1636 // Elevate priority when Home launches for the first time to avoid
1637 // starving at boot time. Staring at a blank home is not cool.
1638 synchronized (mLock) {
1639 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
1640 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
1641 android.os.Process.setThreadPriority(mIsLaunching
1642 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
1644 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
1645 isUpgrade = loadAndBindWorkspace();
1651 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1653 synchronized (mLock) {
1655 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
1656 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1662 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1663 loadAndBindAllApps();
1665 // Restore the default thread priority after we are done loading items
1666 synchronized (mLock) {
1667 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1671 // Update the saved icons if necessary
1672 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
1673 synchronized (sBgLock) {
1674 for (Object key : sBgDbIconCache.keySet()) {
1675 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
1677 sBgDbIconCache.clear();
1680 if (LauncherAppState.isDisableAllApps()) {
1681 // Ensure that all the applications that are in the system are
1682 // represented on the home screen.
1683 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
1684 verifyApplications();
1688 // Clear out this reference, otherwise we end up holding it until all of the
1689 // callback runnables are done.
1692 synchronized (mLock) {
1693 // If we are still the last one to be scheduled, remove ourselves.
1694 if (mLoaderTask == this) {
1697 mIsLoaderTaskRunning = false;
1701 public void stopLocked() {
1702 synchronized (LoaderTask.this) {
1709 * Gets the callbacks object. If we've been stopped, or if the launcher object
1710 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1711 * object that was around when the deferred message was scheduled, and if there's
1712 * a new Callbacks object around then also return null. This will save us from
1713 * calling onto it with data that will be ignored.
1715 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1716 synchronized (mLock) {
1721 if (mCallbacks == null) {
1725 final Callbacks callbacks = mCallbacks.get();
1726 if (callbacks != oldCallbacks) {
1729 if (callbacks == null) {
1730 Log.w(TAG, "no mCallbacks");
1738 private void verifyApplications() {
1739 final Context context = mApp.getContext();
1741 // Cross reference all the applications in our apps list with items in the workspace
1742 ArrayList<ItemInfo> tmpInfos;
1743 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
1744 synchronized (sBgLock) {
1745 for (AppInfo app : mBgAllAppsList.data) {
1746 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
1747 if (tmpInfos.isEmpty()) {
1748 // We are missing an application icon, so add this to the workspace
1750 // This is a rare event, so lets log it
1751 Log.e(TAG, "Missing Application on load: " + app);
1755 if (!added.isEmpty()) {
1756 addAndBindAddedWorkspaceApps(context, added);
1760 // check & update map of what's occupied; used to discard overlapping/invalid items
1761 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
1762 AtomicBoolean deleteOnInvalidPlacement) {
1763 LauncherAppState app = LauncherAppState.getInstance();
1764 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1765 final int countX = (int) grid.numColumns;
1766 final int countY = (int) grid.numRows;
1768 long containerIndex = item.screenId;
1769 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1770 // Return early if we detect that an item is under the hotseat button
1771 if (mCallbacks == null ||
1772 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
1773 deleteOnInvalidPlacement.set(true);
1774 Log.e(TAG, "Error loading shortcut into hotseat " + item
1775 + " into position (" + item.screenId + ":" + item.cellX + ","
1776 + item.cellY + ") occupied by all apps");
1780 final ItemInfo[][] hotseatItems =
1781 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1783 if (item.screenId >= grid.numHotseatIcons) {
1784 Log.e(TAG, "Error loading shortcut " + item
1785 + " into hotseat position " + item.screenId
1786 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
1791 if (hotseatItems != null) {
1792 if (hotseatItems[(int) item.screenId][0] != null) {
1793 Log.e(TAG, "Error loading shortcut into hotseat " + item
1794 + " into position (" + item.screenId + ":" + item.cellX + ","
1795 + item.cellY + ") occupied by "
1796 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1797 [(int) item.screenId][0]);
1800 hotseatItems[(int) item.screenId][0] = item;
1804 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
1805 items[(int) item.screenId][0] = item;
1806 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
1809 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1810 // Skip further checking if it is not the hotseat or workspace container
1814 if (!occupied.containsKey(item.screenId)) {
1815 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
1816 occupied.put(item.screenId, items);
1819 final ItemInfo[][] screens = occupied.get(item.screenId);
1820 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1821 item.cellX < 0 || item.cellY < 0 ||
1822 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1823 Log.e(TAG, "Error loading shortcut " + item
1824 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1825 + item.cellX + "," + item.cellY
1826 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1830 // Check if any workspace icons overlap with each other
1831 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1832 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1833 if (screens[x][y] != null) {
1834 Log.e(TAG, "Error loading shortcut " + item
1835 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1843 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1844 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
1845 screens[x][y] = item;
1852 /** Clears all the sBg data structures */
1853 private void clearSBgDataStructures() {
1854 synchronized (sBgLock) {
1855 sBgWorkspaceItems.clear();
1856 sBgAppWidgets.clear();
1858 sBgItemsIdMap.clear();
1859 sBgDbIconCache.clear();
1860 sBgWorkspaceScreens.clear();
1864 /** Returns whether this is an upgrade path */
1865 private boolean loadWorkspace() {
1867 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1869 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1871 final Context context = mContext;
1872 final ContentResolver contentResolver = context.getContentResolver();
1873 final PackageManager manager = context.getPackageManager();
1874 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1875 final boolean isSafeMode = manager.isSafeMode();
1876 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
1877 final boolean isSdCardReady = context.registerReceiver(null,
1878 new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
1880 LauncherAppState app = LauncherAppState.getInstance();
1881 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1882 int countX = (int) grid.numColumns;
1883 int countY = (int) grid.numRows;
1885 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1886 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1887 LauncherAppState.getLauncherProvider().deleteDatabase();
1890 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1891 // append the user's Launcher2 shortcuts
1892 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1893 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1895 // Make sure the default workspace is loaded
1896 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
1897 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
1900 // This code path is for our old migration code and should no longer be exercised
1901 boolean loadedOldDb = false;
1904 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
1906 synchronized (sBgLock) {
1907 clearSBgDataStructures();
1908 final HashSet<String> installingPkgs = PackageInstallerCompat
1909 .getInstance(mContext).updateAndGetActiveSessionCache();
1911 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
1912 final ArrayList<Long> restoredRows = new ArrayList<Long>();
1913 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
1914 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
1915 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
1917 // +1 for the hotseat (it can be larger than the workspace)
1918 // Load workspace in reverse order to ensure that latest items are loaded first (and
1919 // before any earlier duplicates)
1920 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
1923 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1924 final int intentIndex = c.getColumnIndexOrThrow
1925 (LauncherSettings.Favorites.INTENT);
1926 final int titleIndex = c.getColumnIndexOrThrow
1927 (LauncherSettings.Favorites.TITLE);
1928 final int iconTypeIndex = c.getColumnIndexOrThrow(
1929 LauncherSettings.Favorites.ICON_TYPE);
1930 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1931 final int iconPackageIndex = c.getColumnIndexOrThrow(
1932 LauncherSettings.Favorites.ICON_PACKAGE);
1933 final int iconResourceIndex = c.getColumnIndexOrThrow(
1934 LauncherSettings.Favorites.ICON_RESOURCE);
1935 final int containerIndex = c.getColumnIndexOrThrow(
1936 LauncherSettings.Favorites.CONTAINER);
1937 final int itemTypeIndex = c.getColumnIndexOrThrow(
1938 LauncherSettings.Favorites.ITEM_TYPE);
1939 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1940 LauncherSettings.Favorites.APPWIDGET_ID);
1941 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1942 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
1943 final int screenIndex = c.getColumnIndexOrThrow(
1944 LauncherSettings.Favorites.SCREEN);
1945 final int cellXIndex = c.getColumnIndexOrThrow
1946 (LauncherSettings.Favorites.CELLX);
1947 final int cellYIndex = c.getColumnIndexOrThrow
1948 (LauncherSettings.Favorites.CELLY);
1949 final int spanXIndex = c.getColumnIndexOrThrow
1950 (LauncherSettings.Favorites.SPANX);
1951 final int spanYIndex = c.getColumnIndexOrThrow(
1952 LauncherSettings.Favorites.SPANY);
1953 final int restoredIndex = c.getColumnIndexOrThrow(
1954 LauncherSettings.Favorites.RESTORED);
1955 final int profileIdIndex = c.getColumnIndexOrThrow(
1956 LauncherSettings.Favorites.PROFILE_ID);
1957 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1958 //final int displayModeIndex = c.getColumnIndexOrThrow(
1959 // LauncherSettings.Favorites.DISPLAY_MODE);
1962 String intentDescription;
1963 LauncherAppWidgetInfo appWidgetInfo;
1967 UserHandleCompat user;
1969 while (!mStopped && c.moveToNext()) {
1970 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
1972 int itemType = c.getInt(itemTypeIndex);
1973 boolean restored = 0 != c.getInt(restoredIndex);
1974 boolean allowMissingTarget = false;
1977 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1978 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1979 id = c.getLong(idIndex);
1980 intentDescription = c.getString(intentIndex);
1981 long serialNumber = c.getInt(profileIdIndex);
1982 user = mUserManager.getUserForSerialNumber(serialNumber);
1983 int promiseType = c.getInt(restoredIndex);
1985 // User has been deleted remove the item.
1986 itemsToRemove.add(id);
1990 intent = Intent.parseUri(intentDescription, 0);
1991 ComponentName cn = intent.getComponent();
1992 if (cn != null && cn.getPackageName() != null) {
1993 boolean validPkg = launcherApps.isPackageEnabledForProfile(
1994 cn.getPackageName(), user);
1995 boolean validComponent = validPkg &&
1996 launcherApps.isActivityEnabledForProfile(cn, user);
1998 if (validComponent) {
2000 // no special handling necessary for this item
2001 restoredRows.add(id);
2004 } else if (validPkg) {
2006 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
2007 // We allow auto install apps to have their intent
2008 // updated after an install.
2009 intent = manager.getLaunchIntentForPackage(
2010 cn.getPackageName());
2011 if (intent != null) {
2012 ContentValues values = new ContentValues();
2013 values.put(LauncherSettings.Favorites.INTENT,
2015 String where = BaseColumns._ID + "= ?";
2016 String[] args = {Long.toString(id)};
2017 contentResolver.update(contentUri, values, where, args);
2021 if (intent == null) {
2022 // The app is installed but the component is no
2023 // longer available.
2024 Launcher.addDumpLog(TAG,
2025 "Invalid component removed: " + cn, true);
2026 itemsToRemove.add(id);
2029 // no special handling necessary for this item
2030 restoredRows.add(id);
2033 } else if (restored) {
2034 // Package is not yet available but might be
2036 Launcher.addDumpLog(TAG,
2037 "package not yet restored: " + cn, true);
2039 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
2040 // Restore has started once.
2041 } else if (installingPkgs.contains(cn.getPackageName())) {
2042 // App restore has started. Update the flag
2043 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
2044 ContentValues values = new ContentValues();
2045 values.put(LauncherSettings.Favorites.RESTORED,
2047 String where = BaseColumns._ID + "= ?";
2048 String[] args = {Long.toString(id)};
2049 contentResolver.update(contentUri, values, where, args);
2051 } else if (REMOVE_UNRESTORED_ICONS) {
2052 Launcher.addDumpLog(TAG,
2053 "Unrestored package removed: " + cn, true);
2054 itemsToRemove.add(id);
2057 } else if (isSdCardReady) {
2058 // Do not wait for external media load anymore.
2059 // Log the invalid package, and remove it
2060 Launcher.addDumpLog(TAG,
2061 "Invalid package removed: " + cn, true);
2062 itemsToRemove.add(id);
2065 // SdCard is not ready yet. Package might get available,
2066 // once it is ready.
2067 Launcher.addDumpLog(TAG, "Invalid package: " + cn
2068 + " (check again later)", true);
2069 HashSet<String> pkgs = sPendingPackages.get(user);
2071 pkgs = new HashSet<String>();
2072 sPendingPackages.put(user, pkgs);
2074 pkgs.add(cn.getPackageName());
2075 allowMissingTarget = true;
2076 // Add the icon on the workspace anyway.
2078 } else if (cn == null) {
2079 // For shortcuts with no component, keep them as they are
2080 restoredRows.add(id);
2083 } catch (URISyntaxException e) {
2084 Launcher.addDumpLog(TAG,
2085 "Invalid uri: " + intentDescription, true);
2090 if (user.equals(UserHandleCompat.myUserHandle())) {
2091 Launcher.addDumpLog(TAG,
2092 "constructing info for partially restored package",
2094 info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
2095 intent = getRestoredItemIntent(c, context, intent);
2097 // Don't restore items for other profiles.
2098 itemsToRemove.add(id);
2101 } else if (itemType ==
2102 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2103 info = getShortcutInfo(manager, intent, user, context, c,
2104 iconIndex, titleIndex, mLabelCache, allowMissingTarget);
2106 info = getShortcutInfo(c, context, iconTypeIndex,
2107 iconPackageIndex, iconResourceIndex, iconIndex,
2110 // App shortcuts that used to be automatically added to Launcher
2111 // didn't always have the correct intent flags set, so do that
2113 if (intent.getAction() != null &&
2114 intent.getCategories() != null &&
2115 intent.getAction().equals(Intent.ACTION_MAIN) &&
2116 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
2118 Intent.FLAG_ACTIVITY_NEW_TASK |
2119 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2125 info.intent = intent;
2126 container = c.getInt(containerIndex);
2127 info.container = container;
2128 info.screenId = c.getInt(screenIndex);
2129 info.cellX = c.getInt(cellXIndex);
2130 info.cellY = c.getInt(cellYIndex);
2133 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
2134 info.isDisabled = isSafeMode
2135 && !Utilities.isSystemApp(context, intent);
2137 // check & update map of what's occupied
2138 deleteOnInvalidPlacement.set(false);
2139 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
2140 if (deleteOnInvalidPlacement.get()) {
2141 itemsToRemove.add(id);
2146 switch (container) {
2147 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2148 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2149 sBgWorkspaceItems.add(info);
2152 // Item is in a user folder
2153 FolderInfo folderInfo =
2154 findOrMakeFolder(sBgFolders, container);
2155 folderInfo.add(info);
2158 sBgItemsIdMap.put(info.id, info);
2160 // now that we've loaded everthing re-save it with the
2161 // icon in case it disappears somehow.
2162 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
2164 throw new RuntimeException("Unexpected null ShortcutInfo");
2168 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2169 id = c.getLong(idIndex);
2170 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2172 folderInfo.title = c.getString(titleIndex);
2174 container = c.getInt(containerIndex);
2175 folderInfo.container = container;
2176 folderInfo.screenId = c.getInt(screenIndex);
2177 folderInfo.cellX = c.getInt(cellXIndex);
2178 folderInfo.cellY = c.getInt(cellYIndex);
2179 folderInfo.spanX = 1;
2180 folderInfo.spanY = 1;
2182 // check & update map of what's occupied
2183 deleteOnInvalidPlacement.set(false);
2184 if (!checkItemPlacement(occupied, folderInfo,
2185 deleteOnInvalidPlacement)) {
2186 if (deleteOnInvalidPlacement.get()) {
2187 itemsToRemove.add(id);
2192 switch (container) {
2193 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2194 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2195 sBgWorkspaceItems.add(folderInfo);
2200 // no special handling required for restored folders
2201 restoredRows.add(id);
2204 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2205 sBgFolders.put(folderInfo.id, folderInfo);
2208 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2209 // Read all Launcher-specific widget details
2210 int appWidgetId = c.getInt(appWidgetIdIndex);
2211 String savedProvider = c.getString(appWidgetProviderIndex);
2212 id = c.getLong(idIndex);
2213 final ComponentName component =
2214 ComponentName.unflattenFromString(savedProvider);
2216 final int restoreStatus = c.getInt(restoredIndex);
2217 final boolean isIdValid = (restoreStatus &
2218 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
2220 final boolean wasProviderReady = (restoreStatus &
2221 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
2223 final AppWidgetProviderInfo provider = isIdValid
2224 ? widgets.getAppWidgetInfo(appWidgetId)
2225 : findAppWidgetProviderInfoWithComponent(context, component);
2227 final boolean isProviderReady = isValidProvider(provider);
2228 if (!isSafeMode && wasProviderReady && !isProviderReady) {
2229 String log = "Deleting widget that isn't installed anymore: "
2230 + "id=" + id + " appWidgetId=" + appWidgetId;
2232 Launcher.addDumpLog(TAG, log, false);
2233 itemsToRemove.add(id);
2235 if (isProviderReady) {
2236 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2239 Launcher.getMinSpanForWidget(context, provider);
2240 appWidgetInfo.minSpanX = minSpan[0];
2241 appWidgetInfo.minSpanY = minSpan[1];
2243 int status = restoreStatus;
2244 if (!wasProviderReady) {
2245 // If provider was not previously ready, update the
2246 // status and UI flag.
2248 // Id would be valid only if the widget restore broadcast was received.
2250 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
2252 status &= ~LauncherAppWidgetInfo
2253 .FLAG_PROVIDER_NOT_READY;
2256 appWidgetInfo.restoreStatus = status;
2258 Log.v(TAG, "Widget restore pending id=" + id
2259 + " appWidgetId=" + appWidgetId
2260 + " status =" + restoreStatus);
2261 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2263 appWidgetInfo.restoreStatus = restoreStatus;
2265 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
2266 // Restore has started once.
2267 } else if (installingPkgs.contains(component.getPackageName())) {
2268 // App restore has started. Update the flag
2269 appWidgetInfo.restoreStatus |=
2270 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
2271 } else if (REMOVE_UNRESTORED_ICONS) {
2272 Launcher.addDumpLog(TAG,
2273 "Unrestored widget removed: " + component, true);
2274 itemsToRemove.add(id);
2279 appWidgetInfo.id = id;
2280 appWidgetInfo.screenId = c.getInt(screenIndex);
2281 appWidgetInfo.cellX = c.getInt(cellXIndex);
2282 appWidgetInfo.cellY = c.getInt(cellYIndex);
2283 appWidgetInfo.spanX = c.getInt(spanXIndex);
2284 appWidgetInfo.spanY = c.getInt(spanYIndex);
2286 container = c.getInt(containerIndex);
2287 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2288 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2289 Log.e(TAG, "Widget found where container != " +
2290 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2294 appWidgetInfo.container = c.getInt(containerIndex);
2295 // check & update map of what's occupied
2296 deleteOnInvalidPlacement.set(false);
2297 if (!checkItemPlacement(occupied, appWidgetInfo,
2298 deleteOnInvalidPlacement)) {
2299 if (deleteOnInvalidPlacement.get()) {
2300 itemsToRemove.add(id);
2305 String providerName = appWidgetInfo.providerName.flattenToString();
2306 if (!providerName.equals(savedProvider) ||
2307 (appWidgetInfo.restoreStatus != restoreStatus)) {
2308 ContentValues values = new ContentValues();
2309 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2311 values.put(LauncherSettings.Favorites.RESTORED,
2312 appWidgetInfo.restoreStatus);
2313 String where = BaseColumns._ID + "= ?";
2314 String[] args = {Long.toString(id)};
2315 contentResolver.update(contentUri, values, where, args);
2317 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2318 sBgAppWidgets.add(appWidgetInfo);
2322 } catch (Exception e) {
2323 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
2332 // Break early if we've stopped loading
2334 clearSBgDataStructures();
2338 if (itemsToRemove.size() > 0) {
2339 ContentProviderClient client = contentResolver.acquireContentProviderClient(
2341 // Remove dead items
2342 for (long id : itemsToRemove) {
2343 if (DEBUG_LOADERS) {
2344 Log.d(TAG, "Removed id = " + id);
2346 // Don't notify content observers
2348 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
2350 } catch (RemoteException e) {
2351 Log.w(TAG, "Could not remove id = " + id);
2356 if (restoredRows.size() > 0) {
2357 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
2359 // Update restored items that no longer require special handling
2361 StringBuilder selectionBuilder = new StringBuilder();
2362 selectionBuilder.append(LauncherSettings.Favorites._ID);
2363 selectionBuilder.append(" IN (");
2364 selectionBuilder.append(TextUtils.join(", ", restoredRows));
2365 selectionBuilder.append(")");
2366 ContentValues values = new ContentValues();
2367 values.put(LauncherSettings.Favorites.RESTORED, 0);
2368 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
2369 values, selectionBuilder.toString(), null);
2370 } catch (RemoteException e) {
2371 Log.w(TAG, "Could not update restored rows");
2375 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
2376 context.registerReceiver(new AppsAvailabilityCheck(),
2377 new IntentFilter(StartupReceiver.SYSTEM_READY),
2382 long maxScreenId = 0;
2383 // If we're importing we use the old screen order.
2384 for (ItemInfo item: sBgItemsIdMap.values()) {
2385 long screenId = item.screenId;
2386 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2387 !sBgWorkspaceScreens.contains(screenId)) {
2388 sBgWorkspaceScreens.add(screenId);
2389 if (screenId > maxScreenId) {
2390 maxScreenId = screenId;
2394 Collections.sort(sBgWorkspaceScreens);
2396 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2397 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2398 TextUtils.join(", ", sBgWorkspaceScreens), true);
2400 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
2401 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2403 // Update the max item id after we load an old db
2405 // If we're importing we use the old screen order.
2406 for (ItemInfo item: sBgItemsIdMap.values()) {
2407 maxItemId = Math.max(maxItemId, item.id);
2409 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
2411 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2412 for (Integer i : orderedScreens.keySet()) {
2413 sBgWorkspaceScreens.add(orderedScreens.get(i));
2416 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2417 TextUtils.join(", ", sBgWorkspaceScreens), true);
2419 // Remove any empty screens
2420 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
2421 for (ItemInfo item: sBgItemsIdMap.values()) {
2422 long screenId = item.screenId;
2423 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2424 unusedScreens.contains(screenId)) {
2425 unusedScreens.remove(screenId);
2429 // If there are any empty screens remove them, and update.
2430 if (unusedScreens.size() != 0) {
2432 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
2433 TextUtils.join(", ", unusedScreens), true);
2435 sBgWorkspaceScreens.removeAll(unusedScreens);
2436 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2440 if (DEBUG_LOADERS) {
2441 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2442 Log.d(TAG, "workspace layout: ");
2443 int nScreens = occupied.size();
2444 for (int y = 0; y < countY; y++) {
2447 Iterator<Long> iter = occupied.keySet().iterator();
2448 while (iter.hasNext()) {
2449 long screenId = iter.next();
2453 for (int x = 0; x < countX; x++) {
2454 ItemInfo[][] screen = occupied.get(screenId);
2455 if (x < screen.length && y < screen[x].length) {
2456 line += (screen[x][y] != null) ? "#" : ".";
2462 Log.d(TAG, "[ " + line + " ]");
2469 /** Filters the set of items who are directly or indirectly (via another container) on the
2470 * specified screen. */
2471 private void filterCurrentWorkspaceItems(long currentScreenId,
2472 ArrayList<ItemInfo> allWorkspaceItems,
2473 ArrayList<ItemInfo> currentScreenItems,
2474 ArrayList<ItemInfo> otherScreenItems) {
2475 // Purge any null ItemInfos
2476 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2477 while (iter.hasNext()) {
2478 ItemInfo i = iter.next();
2484 // Order the set of items by their containers first, this allows use to walk through the
2485 // list sequentially, build up a list of containers that are in the specified screen,
2486 // as well as all items in those containers.
2487 Set<Long> itemsOnScreen = new HashSet<Long>();
2488 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2490 public int compare(ItemInfo lhs, ItemInfo rhs) {
2491 return (int) (lhs.container - rhs.container);
2494 for (ItemInfo info : allWorkspaceItems) {
2495 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2496 if (info.screenId == currentScreenId) {
2497 currentScreenItems.add(info);
2498 itemsOnScreen.add(info.id);
2500 otherScreenItems.add(info);
2502 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2503 currentScreenItems.add(info);
2504 itemsOnScreen.add(info.id);
2506 if (itemsOnScreen.contains(info.container)) {
2507 currentScreenItems.add(info);
2508 itemsOnScreen.add(info.id);
2510 otherScreenItems.add(info);
2516 /** Filters the set of widgets which are on the specified screen. */
2517 private void filterCurrentAppWidgets(long currentScreenId,
2518 ArrayList<LauncherAppWidgetInfo> appWidgets,
2519 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2520 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
2522 for (LauncherAppWidgetInfo widget : appWidgets) {
2523 if (widget == null) continue;
2524 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2525 widget.screenId == currentScreenId) {
2526 currentScreenWidgets.add(widget);
2528 otherScreenWidgets.add(widget);
2533 /** Filters the set of folders which are on the specified screen. */
2534 private void filterCurrentFolders(long currentScreenId,
2535 HashMap<Long, ItemInfo> itemsIdMap,
2536 HashMap<Long, FolderInfo> folders,
2537 HashMap<Long, FolderInfo> currentScreenFolders,
2538 HashMap<Long, FolderInfo> otherScreenFolders) {
2540 for (long id : folders.keySet()) {
2541 ItemInfo info = itemsIdMap.get(id);
2542 FolderInfo folder = folders.get(id);
2543 if (info == null || folder == null) continue;
2544 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2545 info.screenId == currentScreenId) {
2546 currentScreenFolders.put(id, folder);
2548 otherScreenFolders.put(id, folder);
2553 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2555 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
2556 final LauncherAppState app = LauncherAppState.getInstance();
2557 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2559 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
2561 public int compare(ItemInfo lhs, ItemInfo rhs) {
2562 int cellCountX = (int) grid.numColumns;
2563 int cellCountY = (int) grid.numRows;
2564 int screenOffset = cellCountX * cellCountY;
2565 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
2566 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
2567 lhs.cellY * cellCountX + lhs.cellX);
2568 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
2569 rhs.cellY * cellCountX + rhs.cellX);
2570 return (int) (lr - rr);
2575 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2576 final ArrayList<Long> orderedScreens) {
2577 final Runnable r = new Runnable() {
2580 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2581 if (callbacks != null) {
2582 callbacks.bindScreens(orderedScreens);
2586 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2589 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2590 final ArrayList<ItemInfo> workspaceItems,
2591 final ArrayList<LauncherAppWidgetInfo> appWidgets,
2592 final HashMap<Long, FolderInfo> folders,
2593 ArrayList<Runnable> deferredBindRunnables) {
2595 final boolean postOnMainThread = (deferredBindRunnables != null);
2597 // Bind the workspace items
2598 int N = workspaceItems.size();
2599 for (int i = 0; i < N; i += ITEMS_CHUNK) {
2600 final int start = i;
2601 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
2602 final Runnable r = new Runnable() {
2605 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2606 if (callbacks != null) {
2607 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2612 if (postOnMainThread) {
2613 synchronized (deferredBindRunnables) {
2614 deferredBindRunnables.add(r);
2617 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2622 if (!folders.isEmpty()) {
2623 final Runnable r = new Runnable() {
2625 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2626 if (callbacks != null) {
2627 callbacks.bindFolders(folders);
2631 if (postOnMainThread) {
2632 synchronized (deferredBindRunnables) {
2633 deferredBindRunnables.add(r);
2636 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2640 // Bind the widgets, one at a time
2641 N = appWidgets.size();
2642 for (int i = 0; i < N; i++) {
2643 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2644 final Runnable r = new Runnable() {
2646 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2647 if (callbacks != null) {
2648 callbacks.bindAppWidget(widget);
2652 if (postOnMainThread) {
2653 deferredBindRunnables.add(r);
2655 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2661 * Binds all loaded data to actual views on the main thread.
2663 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
2664 final long t = SystemClock.uptimeMillis();
2667 // Don't use these two variables in any of the callback runnables.
2668 // Otherwise we hold a reference to them.
2669 final Callbacks oldCallbacks = mCallbacks.get();
2670 if (oldCallbacks == null) {
2671 // This launcher has exited and nobody bothered to tell us. Just bail.
2672 Log.w(TAG, "LoaderTask running with no launcher");
2676 // Save a copy of all the bg-thread collections
2677 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2678 ArrayList<LauncherAppWidgetInfo> appWidgets =
2679 new ArrayList<LauncherAppWidgetInfo>();
2680 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2681 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
2682 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
2683 synchronized (sBgLock) {
2684 workspaceItems.addAll(sBgWorkspaceItems);
2685 appWidgets.addAll(sBgAppWidgets);
2686 folders.putAll(sBgFolders);
2687 itemsIdMap.putAll(sBgItemsIdMap);
2688 orderedScreenIds.addAll(sBgWorkspaceScreens);
2691 final boolean isLoadingSynchronously =
2692 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
2693 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
2694 oldCallbacks.getCurrentWorkspaceScreen();
2695 if (currScreen >= orderedScreenIds.size()) {
2696 // There may be no workspace screens (just hotseat items and an empty page).
2697 currScreen = PagedView.INVALID_RESTORE_PAGE;
2699 final int currentScreen = currScreen;
2700 final long currentScreenId = currentScreen < 0
2701 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
2703 // Load all the items that are on the current page first (and in the process, unbind
2704 // all the existing workspace items before we call startBinding() below.
2705 unbindWorkspaceItemsOnMainThread();
2707 // Separate the items that are on the current screen, and all the other remaining items
2708 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2709 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2710 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
2711 new ArrayList<LauncherAppWidgetInfo>();
2712 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
2713 new ArrayList<LauncherAppWidgetInfo>();
2714 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2715 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2717 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
2718 otherWorkspaceItems);
2719 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
2721 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
2723 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2724 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2726 // Tell the workspace that we're about to start binding items
2727 r = new Runnable() {
2729 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2730 if (callbacks != null) {
2731 callbacks.startBinding();
2735 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2737 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2739 // Load items on the current page
2740 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
2741 currentFolders, null);
2742 if (isLoadingSynchronously) {
2743 r = new Runnable() {
2745 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2746 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
2747 callbacks.onPageBoundSynchronously(currentScreen);
2751 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2754 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2755 // work until after the first render)
2756 synchronized (mDeferredBindRunnables) {
2757 mDeferredBindRunnables.clear();
2759 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
2760 (isLoadingSynchronously ? mDeferredBindRunnables : null));
2762 // Tell the workspace that we're done binding items
2763 r = new Runnable() {
2765 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2766 if (callbacks != null) {
2767 callbacks.finishBindingItems(isUpgradePath);
2770 // If we're profiling, ensure this is the last thing in the queue.
2771 if (DEBUG_LOADERS) {
2772 Log.d(TAG, "bound workspace in "
2773 + (SystemClock.uptimeMillis()-t) + "ms");
2776 mIsLoadingAndBindingWorkspace = false;
2779 if (isLoadingSynchronously) {
2780 synchronized (mDeferredBindRunnables) {
2781 mDeferredBindRunnables.add(r);
2784 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2788 private void loadAndBindAllApps() {
2789 if (DEBUG_LOADERS) {
2790 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2792 if (!mAllAppsLoaded) {
2794 synchronized (LoaderTask.this) {
2798 mAllAppsLoaded = true;
2805 private void onlyBindAllApps() {
2806 final Callbacks oldCallbacks = mCallbacks.get();
2807 if (oldCallbacks == null) {
2808 // This launcher has exited and nobody bothered to tell us. Just bail.
2809 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2814 @SuppressWarnings("unchecked")
2815 final ArrayList<AppInfo> list
2816 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
2817 Runnable r = new Runnable() {
2819 final long t = SystemClock.uptimeMillis();
2820 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2821 if (callbacks != null) {
2822 callbacks.bindAllApplications(list);
2824 if (DEBUG_LOADERS) {
2825 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
2826 + (SystemClock.uptimeMillis()-t) + "ms");
2830 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
2831 if (isRunningOnMainThread) {
2838 private void loadAllApps() {
2839 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2841 final Callbacks oldCallbacks = mCallbacks.get();
2842 if (oldCallbacks == null) {
2843 // This launcher has exited and nobody bothered to tell us. Just bail.
2844 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
2848 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2849 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2851 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2853 // Clear the list of apps
2854 mBgAllAppsList.clear();
2855 for (UserHandleCompat user : profiles) {
2856 // Query for the set of apps
2857 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2858 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
2859 if (DEBUG_LOADERS) {
2860 Log.d(TAG, "getActivityList took "
2861 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
2862 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
2864 // Fail if we don't have any apps
2865 if (apps == null || apps.isEmpty()) {
2868 // Sort the applications by name
2869 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2870 Collections.sort(apps,
2871 new LauncherModel.ShortcutNameComparator(mLabelCache));
2872 if (DEBUG_LOADERS) {
2873 Log.d(TAG, "sort took "
2874 + (SystemClock.uptimeMillis()-sortTime) + "ms");
2877 // Create the ApplicationInfos
2878 for (int i = 0; i < apps.size(); i++) {
2879 LauncherActivityInfoCompat app = apps.get(i);
2880 // This builds the icon bitmaps.
2881 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
2884 // Huh? Shouldn't this be inside the Runnable below?
2885 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2886 mBgAllAppsList.added = new ArrayList<AppInfo>();
2888 // Post callback on main thread
2889 mHandler.post(new Runnable() {
2891 final long bindTime = SystemClock.uptimeMillis();
2892 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2893 if (callbacks != null) {
2894 callbacks.bindAllApplications(added);
2895 if (DEBUG_LOADERS) {
2896 Log.d(TAG, "bound " + added.size() + " apps in "
2897 + (SystemClock.uptimeMillis() - bindTime) + "ms");
2900 Log.i(TAG, "not binding apps: no Launcher activity");
2905 if (DEBUG_LOADERS) {
2906 Log.d(TAG, "Icons processed in "
2907 + (SystemClock.uptimeMillis() - loadTime) + "ms");
2911 public void dumpState() {
2912 synchronized (sBgLock) {
2913 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2914 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2915 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2916 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2917 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2922 void enqueuePackageUpdated(PackageUpdatedTask task) {
2926 private class AppsAvailabilityCheck extends BroadcastReceiver {
2929 public void onReceive(Context context, Intent intent) {
2930 synchronized (sBgLock) {
2931 final LauncherAppsCompat launcherApps = LauncherAppsCompat
2932 .getInstance(mApp.getContext());
2933 ArrayList<String> packagesRemoved;
2934 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2935 UserHandleCompat user = entry.getKey();
2936 packagesRemoved = new ArrayList<String>();
2937 for (String pkg : entry.getValue()) {
2938 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
2939 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
2940 packagesRemoved.add(pkg);
2943 if (!packagesRemoved.isEmpty()) {
2944 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
2945 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
2948 sPendingPackages.clear();
2954 * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
2955 * runnable was missed by the launcher.
2957 public void recheckRestoredItems(final Context context) {
2958 Runnable r = new Runnable() {
2962 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
2963 HashSet<String> installedPackages = new HashSet<String>();
2964 UserHandleCompat user = UserHandleCompat.myUserHandle();
2965 synchronized(sBgLock) {
2966 for (ItemInfo info : sBgItemsIdMap.values()) {
2967 if (info instanceof ShortcutInfo) {
2968 ShortcutInfo si = (ShortcutInfo) info;
2969 if (si.isPromise() && si.getTargetComponent() != null
2970 && launcherApps.isPackageEnabledForProfile(
2971 si.getTargetComponent().getPackageName(), user)) {
2972 installedPackages.add(si.getTargetComponent().getPackageName());
2974 } else if (info instanceof LauncherAppWidgetInfo) {
2975 LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) info;
2976 if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
2977 && launcherApps.isPackageEnabledForProfile(
2978 widget.providerName.getPackageName(), user)) {
2979 installedPackages.add(widget.providerName.getPackageName());
2985 if (!installedPackages.isEmpty()) {
2986 final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
2987 for (String pkg : installedPackages) {
2988 for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
2989 restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
2993 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
2994 if (!restoredApps.isEmpty()) {
2995 mHandler.post(new Runnable() {
2997 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2998 if (callbacks == cb && cb != null) {
2999 callbacks.bindAppsRestored(restoredApps);
3011 private class PackageUpdatedTask implements Runnable {
3014 UserHandleCompat mUser;
3016 public static final int OP_NONE = 0;
3017 public static final int OP_ADD = 1;
3018 public static final int OP_UPDATE = 2;
3019 public static final int OP_REMOVE = 3; // uninstlled
3020 public static final int OP_UNAVAILABLE = 4; // external media unmounted
3023 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
3025 mPackages = packages;
3030 final Context context = mApp.getContext();
3032 final String[] packages = mPackages;
3033 final int N = packages.length;
3036 for (int i=0; i<N; i++) {
3037 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
3038 mIconCache.remove(packages[i], mUser);
3039 mBgAllAppsList.addPackage(context, packages[i], mUser);
3043 for (int i=0; i<N; i++) {
3044 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
3045 mBgAllAppsList.updatePackage(context, packages[i], mUser);
3046 WidgetPreviewLoader.removePackageFromDb(
3047 mApp.getWidgetPreviewCacheDb(), packages[i]);
3051 case OP_UNAVAILABLE:
3052 for (int i=0; i<N; i++) {
3053 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
3054 mBgAllAppsList.removePackage(packages[i], mUser);
3055 WidgetPreviewLoader.removePackageFromDb(
3056 mApp.getWidgetPreviewCacheDb(), packages[i]);
3061 ArrayList<AppInfo> added = null;
3062 ArrayList<AppInfo> modified = null;
3063 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
3065 if (mBgAllAppsList.added.size() > 0) {
3066 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
3067 mBgAllAppsList.added.clear();
3069 if (mBgAllAppsList.modified.size() > 0) {
3070 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
3071 mBgAllAppsList.modified.clear();
3073 if (mBgAllAppsList.removed.size() > 0) {
3074 removedApps.addAll(mBgAllAppsList.removed);
3075 mBgAllAppsList.removed.clear();
3078 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
3079 if (callbacks == null) {
3080 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
3084 if (added != null) {
3085 // Ensure that we add all the workspace applications to the db
3086 if (LauncherAppState.isDisableAllApps()) {
3087 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
3088 addAndBindAddedWorkspaceApps(context, addedInfos);
3090 addAppsToAllApps(context, added);
3094 if (modified != null) {
3095 final ArrayList<AppInfo> modifiedFinal = modified;
3097 // Update the launcher db to reflect the changes
3098 for (AppInfo a : modifiedFinal) {
3099 ArrayList<ItemInfo> infos =
3100 getItemInfoForComponentName(a.componentName, mUser);
3101 for (ItemInfo i : infos) {
3102 if (isShortcutInfoUpdateable(i)) {
3103 ShortcutInfo info = (ShortcutInfo) i;
3104 info.title = a.title.toString();
3105 info.contentDescription = a.contentDescription;
3106 updateItemInDatabase(context, info);
3111 mHandler.post(new Runnable() {
3113 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3114 if (callbacks == cb && cb != null) {
3115 callbacks.bindAppsUpdated(modifiedFinal);
3121 final ArrayList<String> removedPackageNames =
3122 new ArrayList<String>();
3123 if (mOp == OP_REMOVE) {
3124 // Mark all packages in the broadcast to be removed
3125 removedPackageNames.addAll(Arrays.asList(packages));
3126 } else if (mOp == OP_UPDATE) {
3127 // Mark disabled packages in the broadcast to be removed
3128 final PackageManager pm = context.getPackageManager();
3129 for (int i=0; i<N; i++) {
3130 if (isPackageDisabled(context, packages[i], mUser)) {
3131 removedPackageNames.add(packages[i]);
3135 // Remove all the components associated with this package
3136 for (String pn : removedPackageNames) {
3137 deletePackageFromDatabase(context, pn, mUser);
3139 // Remove all the specific components
3140 for (AppInfo a : removedApps) {
3141 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
3142 deleteItemsFromDatabase(context, infos);
3144 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
3145 // Remove any queued items from the install queue
3146 String spKey = LauncherAppState.getSharedPreferencesKey();
3147 SharedPreferences sp =
3148 context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
3149 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
3150 // Call the components-removed callback
3151 mHandler.post(new Runnable() {
3153 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3154 if (callbacks == cb && cb != null) {
3155 callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
3161 final ArrayList<Object> widgetsAndShortcuts =
3162 getSortedWidgetsAndShortcuts(context);
3163 mHandler.post(new Runnable() {
3166 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3167 if (callbacks == cb && cb != null) {
3168 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
3173 // Write all the logs to disk
3174 mHandler.post(new Runnable() {
3176 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3177 if (callbacks == cb && cb != null) {
3178 callbacks.dumpLogsToLocalData();
3185 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
3186 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
3187 PackageManager packageManager = context.getPackageManager();
3188 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
3189 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
3191 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
3192 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
3193 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
3194 return widgetsAndShortcuts;
3197 private static boolean isPackageDisabled(Context context, String packageName,
3198 UserHandleCompat user) {
3199 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3200 return !launcherApps.isPackageEnabledForProfile(packageName, user);
3203 public static boolean isValidPackageActivity(Context context, ComponentName cn,
3204 UserHandleCompat user) {
3208 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3209 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
3212 return launcherApps.isActivityEnabledForProfile(cn, user);
3215 public static boolean isValidPackage(Context context, String packageName,
3216 UserHandleCompat user) {
3217 if (packageName == null) {
3220 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3221 return launcherApps.isPackageEnabledForProfile(packageName, user);
3225 * Make an ShortcutInfo object for a restored application or shortcut item that points
3226 * to a package that is not yet installed on the system.
3228 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
3230 final ShortcutInfo info = new ShortcutInfo();
3231 info.user = UserHandleCompat.myUserHandle();
3232 mIconCache.getTitleAndIcon(info, intent, info.user, true);
3234 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
3235 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
3236 if (!TextUtils.isEmpty(title)) {
3239 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
3240 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
3241 if (TextUtils.isEmpty(info.title)) {
3242 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
3244 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
3246 throw new InvalidParameterException("Invalid restoreType " + promiseType);
3249 info.contentDescription = mUserManager.getBadgedLabelForUser(
3250 info.title.toString(), info.user);
3251 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3252 info.promisedIntent = intent;
3257 * Make an Intent object for a restored application or shortcut item that points
3258 * to the market page for the item.
3260 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
3261 ComponentName componentName = intent.getComponent();
3262 return getMarketIntent(componentName.getPackageName());
3265 static Intent getMarketIntent(String packageName) {
3266 return new Intent(Intent.ACTION_VIEW)
3267 .setData(new Uri.Builder()
3269 .authority("details")
3270 .appendQueryParameter("id", packageName)
3275 * This is called from the code that adds shortcuts from the intent receiver. This
3276 * doesn't have a Cursor, but
3278 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3279 UserHandleCompat user, Context context) {
3280 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
3284 * Make an ShortcutInfo object for a shortcut that is an application.
3286 * If c is not null, then it will be used to fill in missing data like the title and icon.
3288 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3289 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
3290 HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
3292 Log.d(TAG, "Null user found in getShortcutInfo");
3296 ComponentName componentName = intent.getComponent();
3297 if (componentName == null) {
3298 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
3302 Intent newIntent = new Intent(intent.getAction(), null);
3303 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3304 newIntent.setComponent(componentName);
3305 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
3306 if ((lai == null) && !allowMissingTarget) {
3307 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
3311 final ShortcutInfo info = new ShortcutInfo();
3313 // the resource -- This may implicitly give us back the fallback icon,
3314 // but don't worry about that. All we're doing with usingFallbackIcon is
3315 // to avoid saving lots of copies of that in the database, and most apps
3316 // have icons anyway.
3317 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
3322 icon = getIconFromCursor(c, iconIndex, context);
3325 // the fallback icon
3327 icon = mIconCache.getDefaultIcon(user);
3328 info.usingFallbackIcon = true;
3333 if (labelCache != null) {
3334 info.title = labelCache.get(componentName);
3337 // from the resource
3338 if (info.title == null && lai != null) {
3339 info.title = lai.getLabel();
3340 if (labelCache != null) {
3341 labelCache.put(componentName, info.title);
3345 if (info.title == null) {
3347 info.title = c.getString(titleIndex);
3350 // fall back to the class name of the activity
3351 if (info.title == null) {
3352 info.title = componentName.getClassName();
3354 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
3356 info.contentDescription = mUserManager.getBadgedLabelForUser(
3357 info.title.toString(), info.user);
3361 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
3363 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
3364 for (ItemInfo i : infos) {
3365 if (i instanceof ShortcutInfo) {
3366 ShortcutInfo info = (ShortcutInfo) i;
3367 ComponentName cn = info.getTargetComponent();
3368 if (cn != null && f.filterItem(null, info, cn)) {
3371 } else if (i instanceof FolderInfo) {
3372 FolderInfo info = (FolderInfo) i;
3373 for (ShortcutInfo s : info.contents) {
3374 ComponentName cn = s.getTargetComponent();
3375 if (cn != null && f.filterItem(info, s, cn)) {
3379 } else if (i instanceof LauncherAppWidgetInfo) {
3380 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
3381 ComponentName cn = info.providerName;
3382 if (cn != null && f.filterItem(null, info, cn)) {
3387 return new ArrayList<ItemInfo>(filtered);
3390 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
3391 final UserHandleCompat user) {
3392 ItemInfoFilter filter = new ItemInfoFilter() {
3394 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
3395 if (info.user == null) {
3396 return cn.equals(cname);
3398 return cn.equals(cname) && info.user.equals(user);
3402 return filterItemInfos(sBgItemsIdMap.values(), filter);
3405 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
3406 if (i instanceof ShortcutInfo) {
3407 ShortcutInfo info = (ShortcutInfo) i;
3408 // We need to check for ACTION_MAIN otherwise getComponent() might
3409 // return null for some shortcuts (for instance, for shortcuts to
3411 Intent intent = info.intent;
3412 ComponentName name = intent.getComponent();
3413 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
3414 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3417 // placeholder shortcuts get special treatment, let them through too.
3418 if (info.isPromise()) {
3426 * Make an ShortcutInfo object for a shortcut that isn't an application.
3428 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
3429 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
3433 final ShortcutInfo info = new ShortcutInfo();
3434 // Non-app shortcuts are only supported for current user.
3435 info.user = UserHandleCompat.myUserHandle();
3436 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
3438 // TODO: If there's an explicit component and we can't install that, delete it.
3440 info.title = c.getString(titleIndex);
3442 int iconType = c.getInt(iconTypeIndex);
3444 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
3445 String packageName = c.getString(iconPackageIndex);
3446 String resourceName = c.getString(iconResourceIndex);
3447 PackageManager packageManager = context.getPackageManager();
3448 info.customIcon = false;
3451 Resources resources = packageManager.getResourcesForApplication(packageName);
3452 if (resources != null) {
3453 final int id = resources.getIdentifier(resourceName, null, null);
3454 icon = Utilities.createIconBitmap(
3455 mIconCache.getFullResIcon(resources, id), context);
3457 } catch (Exception e) {
3458 // drop this. we have other places to look for icons
3462 icon = getIconFromCursor(c, iconIndex, context);
3464 // the fallback icon
3466 icon = mIconCache.getDefaultIcon(info.user);
3467 info.usingFallbackIcon = true;
3470 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
3471 icon = getIconFromCursor(c, iconIndex, context);
3473 icon = mIconCache.getDefaultIcon(info.user);
3474 info.customIcon = false;
3475 info.usingFallbackIcon = true;
3477 info.customIcon = true;
3481 icon = mIconCache.getDefaultIcon(info.user);
3482 info.usingFallbackIcon = true;
3483 info.customIcon = false;
3490 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
3491 @SuppressWarnings("all") // suppress dead code warning
3492 final boolean debug = false;
3494 Log.d(TAG, "getIconFromCursor app="
3495 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
3497 byte[] data = c.getBlob(iconIndex);
3499 return Utilities.createIconBitmap(
3500 BitmapFactory.decodeByteArray(data, 0, data.length), context);
3501 } catch (Exception e) {
3506 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
3507 int cellX, int cellY, boolean notify) {
3508 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
3512 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
3518 * Attempts to find an AppWidgetProviderInfo that matches the given component.
3520 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
3521 ComponentName component) {
3522 List<AppWidgetProviderInfo> widgets =
3523 AppWidgetManager.getInstance(context).getInstalledProviders();
3524 for (AppWidgetProviderInfo info : widgets) {
3525 if (info.provider.equals(component)) {
3532 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
3533 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3534 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3535 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3537 if (intent == null) {
3538 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3539 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3544 boolean customIcon = false;
3545 ShortcutIconResource iconResource = null;
3547 if (bitmap != null && bitmap instanceof Bitmap) {
3548 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
3551 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3552 if (extra != null && extra instanceof ShortcutIconResource) {
3554 iconResource = (ShortcutIconResource) extra;
3555 final PackageManager packageManager = context.getPackageManager();
3556 Resources resources = packageManager.getResourcesForApplication(
3557 iconResource.packageName);
3558 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
3559 icon = Utilities.createIconBitmap(
3560 mIconCache.getFullResIcon(resources, id),
3562 } catch (Exception e) {
3563 Log.w(TAG, "Could not load shortcut icon: " + extra);
3568 final ShortcutInfo info = new ShortcutInfo();
3570 // Only support intents for current user for now. Intents sent from other
3571 // users wouldn't get here without intent forwarding anyway.
3572 info.user = UserHandleCompat.myUserHandle();
3574 if (fallbackIcon != null) {
3575 icon = fallbackIcon;
3577 icon = mIconCache.getDefaultIcon(info.user);
3578 info.usingFallbackIcon = true;
3584 info.contentDescription = mUserManager.getBadgedLabelForUser(
3585 info.title.toString(), info.user);
3586 info.intent = intent;
3587 info.customIcon = customIcon;
3588 info.iconResource = iconResource;
3593 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
3595 // If apps can't be on SD, don't even bother.
3596 if (!mAppsCanBeOnRemoveableStorage) {
3599 // If this icon doesn't have a custom icon, check to see
3600 // what's stored in the DB, and if it doesn't match what
3601 // we're going to show, store what we are going to show back
3602 // into the DB. We do this so when we're loading, if the
3603 // package manager can't find an icon (for example because
3604 // the app is on SD) then we can use that instead.
3605 if (!info.customIcon && !info.usingFallbackIcon) {
3606 cache.put(info, c.getBlob(iconIndex));
3611 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3612 boolean needSave = false;
3615 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3616 Bitmap loaded = info.getIcon(mIconCache);
3617 needSave = !saved.sameAs(loaded);
3621 } catch (Exception e) {
3625 Log.d(TAG, "going to save icon bitmap for info=" + info);
3626 // This is slower than is ideal, but this only happens once
3627 // or when the app is updated with a new icon.
3628 updateItemInDatabase(context, info);
3633 * Return an existing FolderInfo object if we have encountered this ID previously,
3634 * or make a new one.
3636 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
3637 // See if a placeholder was created for us already
3638 FolderInfo folderInfo = folders.get(id);
3639 if (folderInfo == null) {
3640 // No placeholder -- create a new instance
3641 folderInfo = new FolderInfo();
3642 folders.put(id, folderInfo);
3647 public static final Comparator<AppInfo> getAppNameComparator() {
3648 final Collator collator = Collator.getInstance();
3649 return new Comparator<AppInfo>() {
3650 public final int compare(AppInfo a, AppInfo b) {
3651 if (a.user.equals(b.user)) {
3652 int result = collator.compare(a.title.toString().trim(),
3653 b.title.toString().trim());
3655 result = a.componentName.compareTo(b.componentName);
3659 // TODO Need to figure out rules for sorting
3660 // profiles, this puts work second.
3661 return a.user.toString().compareTo(b.user.toString());
3666 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
3667 = new Comparator<AppInfo>() {
3668 public final int compare(AppInfo a, AppInfo b) {
3669 if (a.firstInstallTime < b.firstInstallTime) return 1;
3670 if (a.firstInstallTime > b.firstInstallTime) return -1;
3674 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3675 if (info.activityInfo != null) {
3676 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3678 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3681 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
3682 private Collator mCollator;
3683 private HashMap<Object, CharSequence> mLabelCache;
3684 ShortcutNameComparator(PackageManager pm) {
3685 mLabelCache = new HashMap<Object, CharSequence>();
3686 mCollator = Collator.getInstance();
3688 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
3689 mLabelCache = labelCache;
3690 mCollator = Collator.getInstance();
3692 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
3693 String labelA, labelB;
3694 ComponentName keyA = a.getComponentName();
3695 ComponentName keyB = b.getComponentName();
3696 if (mLabelCache.containsKey(keyA)) {
3697 labelA = mLabelCache.get(keyA).toString();
3699 labelA = a.getLabel().toString().trim();
3701 mLabelCache.put(keyA, labelA);
3703 if (mLabelCache.containsKey(keyB)) {
3704 labelB = mLabelCache.get(keyB).toString();
3706 labelB = b.getLabel().toString().trim();
3708 mLabelCache.put(keyB, labelB);
3710 return mCollator.compare(labelA, labelB);
3713 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
3714 private final AppWidgetManagerCompat mManager;
3715 private final PackageManager mPackageManager;
3716 private final HashMap<Object, String> mLabelCache;
3717 private final Collator mCollator;
3719 WidgetAndShortcutNameComparator(Context context) {
3720 mManager = AppWidgetManagerCompat.getInstance(context);
3721 mPackageManager = context.getPackageManager();
3722 mLabelCache = new HashMap<Object, String>();
3723 mCollator = Collator.getInstance();
3725 public final int compare(Object a, Object b) {
3726 String labelA, labelB;
3727 if (mLabelCache.containsKey(a)) {
3728 labelA = mLabelCache.get(a);
3730 labelA = (a instanceof AppWidgetProviderInfo)
3731 ? mManager.loadLabel((AppWidgetProviderInfo) a)
3732 : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
3733 mLabelCache.put(a, labelA);
3735 if (mLabelCache.containsKey(b)) {
3736 labelB = mLabelCache.get(b);
3738 labelB = (b instanceof AppWidgetProviderInfo)
3739 ? mManager.loadLabel((AppWidgetProviderInfo) b)
3740 : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
3741 mLabelCache.put(b, labelB);
3743 return mCollator.compare(labelA, labelB);
3747 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3748 return (provider != null) && (provider.provider != null)
3749 && (provider.provider.getPackageName() != null);
3752 public void dumpState() {
3753 Log.d(TAG, "mCallbacks=" + mCallbacks);
3754 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3755 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3756 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3757 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
3758 if (mLoaderTask != null) {
3759 mLoaderTask.dumpState();
3761 Log.d(TAG, "mLoaderTask=null");