LauncherModel.java revision 414300a79d140f8c2c8760d9adab750f69ffeafd
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package com.android.launcher3; 18 19import android.app.SearchManager; 20import android.appwidget.AppWidgetManager; 21import android.appwidget.AppWidgetProviderInfo; 22import android.content.*; 23import android.content.Intent.ShortcutIconResource; 24import android.content.pm.ActivityInfo; 25import android.content.pm.PackageInfo; 26import android.content.pm.PackageManager; 27import android.content.pm.PackageManager.NameNotFoundException; 28import android.content.pm.ResolveInfo; 29import android.content.res.Configuration; 30import android.content.res.Resources; 31import android.database.Cursor; 32import android.graphics.Bitmap; 33import android.graphics.BitmapFactory; 34import android.net.Uri; 35import android.os.Environment; 36import android.os.Handler; 37import android.os.HandlerThread; 38import android.os.Parcelable; 39import android.os.Process; 40import android.os.RemoteException; 41import android.os.SystemClock; 42import android.util.Log; 43import android.util.Pair; 44import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData; 45 46import java.lang.ref.WeakReference; 47import java.net.URISyntaxException; 48import java.text.Collator; 49import java.util.ArrayList; 50import java.util.Arrays; 51import java.util.Collection; 52import java.util.Collections; 53import java.util.Comparator; 54import java.util.HashMap; 55import java.util.HashSet; 56import java.util.Iterator; 57import java.util.List; 58import java.util.Set; 59import java.util.TreeMap; 60 61/** 62 * Maintains in-memory state of the Launcher. It is expected that there should be only one 63 * LauncherModel object held in a static. Also provide APIs for updating the database state 64 * for the Launcher. 65 */ 66public class LauncherModel extends BroadcastReceiver { 67 static final boolean DEBUG_LOADERS = false; 68 static final String TAG = "Launcher.Model"; 69 70 // true = use a "More Apps" folder for non-workspace apps on upgrade 71 // false = strew non-workspace apps across the workspace on upgrade 72 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false; 73 74 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons 75 private final boolean mAppsCanBeOnRemoveableStorage; 76 77 private final LauncherAppState mApp; 78 private final Object mLock = new Object(); 79 private DeferredHandler mHandler = new DeferredHandler(); 80 private LoaderTask mLoaderTask; 81 private boolean mIsLoaderTaskRunning; 82 private volatile boolean mFlushingWorkerThread; 83 84 // Specific runnable types that are run on the main thread deferred handler, this allows us to 85 // clear all queued binding runnables when the Launcher activity is destroyed. 86 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0; 87 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1; 88 89 90 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); 91 static { 92 sWorkerThread.start(); 93 } 94 private static final Handler sWorker = new Handler(sWorkerThread.getLooper()); 95 96 // We start off with everything not loaded. After that, we assume that 97 // our monitoring of the package manager provides all updates and we never 98 // need to do a requery. These are only ever touched from the loader thread. 99 private boolean mWorkspaceLoaded; 100 private boolean mAllAppsLoaded; 101 102 // When we are loading pages synchronously, we can't just post the binding of items on the side 103 // pages as this delays the rotation process. Instead, we wait for a callback from the first 104 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start 105 // a normal load, we also clear this set of Runnables. 106 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>(); 107 108 private WeakReference<Callbacks> mCallbacks; 109 110 // < only access in worker thread > 111 private AllAppsList mBgAllAppsList; 112 113 // The lock that must be acquired before referencing any static bg data structures. Unlike 114 // other locks, this one can generally be held long-term because we never expect any of these 115 // static data structures to be referenced outside of the worker thread except on the first 116 // load after configuration change. 117 static final Object sBgLock = new Object(); 118 119 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by 120 // LauncherModel to their ids 121 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>(); 122 123 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts 124 // created by LauncherModel that are directly on the home screen (however, no widgets or 125 // shortcuts within folders). 126 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>(); 127 128 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget() 129 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets = 130 new ArrayList<LauncherAppWidgetInfo>(); 131 132 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders() 133 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>(); 134 135 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database 136 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>(); 137 138 // sBgWorkspaceScreens is the ordered set of workspace screens. 139 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>(); 140 141 // </ only access in worker thread > 142 143 private IconCache mIconCache; 144 private Bitmap mDefaultIcon; 145 146 protected int mPreviousConfigMcc; 147 148 public interface Callbacks { 149 public boolean setLoadOnResume(); 150 public int getCurrentWorkspaceScreen(); 151 public void startBinding(); 152 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, 153 boolean forceAnimateIcons); 154 public void bindScreens(ArrayList<Long> orderedScreenIds); 155 public void bindAddScreens(ArrayList<Long> orderedScreenIds); 156 public void bindFolders(HashMap<Long,FolderInfo> folders); 157 public void finishBindingItems(boolean upgradePath); 158 public void bindAppWidget(LauncherAppWidgetInfo info); 159 public void bindAllApplications(ArrayList<ApplicationInfo> apps); 160 public void bindAppsAdded(ArrayList<Long> newScreens, 161 ArrayList<ItemInfo> addNotAnimated, 162 ArrayList<ItemInfo> addAnimated); 163 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); 164 public void bindComponentsRemoved(ArrayList<String> packageNames, 165 ArrayList<ApplicationInfo> appInfos, 166 boolean matchPackageNamesOnly); 167 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts); 168 public void bindSearchablesChanged(); 169 public void onPageBoundSynchronously(int page); 170 } 171 172 public interface ItemInfoFilter { 173 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn); 174 } 175 176 LauncherModel(LauncherAppState app, IconCache iconCache) { 177 final Context context = app.getContext(); 178 179 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable(); 180 mApp = app; 181 mBgAllAppsList = new AllAppsList(iconCache); 182 mIconCache = iconCache; 183 184 mDefaultIcon = Utilities.createIconBitmap( 185 mIconCache.getFullResDefaultActivityIcon(), context); 186 187 final Resources res = context.getResources(); 188 Configuration config = res.getConfiguration(); 189 mPreviousConfigMcc = config.mcc; 190 } 191 192 /** Runs the specified runnable immediately if called from the main thread, otherwise it is 193 * posted on the main thread handler. */ 194 private void runOnMainThread(Runnable r) { 195 runOnMainThread(r, 0); 196 } 197 private void runOnMainThread(Runnable r, int type) { 198 if (sWorkerThread.getThreadId() == Process.myTid()) { 199 // If we are on the worker thread, post onto the main handler 200 mHandler.post(r); 201 } else { 202 r.run(); 203 } 204 } 205 206 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is 207 * posted on the worker thread handler. */ 208 private static void runOnWorkerThread(Runnable r) { 209 if (sWorkerThread.getThreadId() == Process.myTid()) { 210 r.run(); 211 } else { 212 // If we are not on the worker thread, then post to the worker handler 213 sWorker.post(r); 214 } 215 } 216 217 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy, 218 long screen) { 219 LauncherAppState app = LauncherAppState.getInstance(); 220 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); 221 final int xCount = (int) grid.numColumns; 222 final int yCount = (int) grid.numRows; 223 boolean[][] occupied = new boolean[xCount][yCount]; 224 225 int cellX, cellY, spanX, spanY; 226 for (int i = 0; i < items.size(); ++i) { 227 final ItemInfo item = items.get(i); 228 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 229 if (item.screenId == screen) { 230 cellX = item.cellX; 231 cellY = item.cellY; 232 spanX = item.spanX; 233 spanY = item.spanY; 234 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) { 235 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) { 236 occupied[x][y] = true; 237 } 238 } 239 } 240 } 241 } 242 243 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied); 244 } 245 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name, 246 Intent launchIntent, 247 int firstScreenIndex, 248 ArrayList<Long> workspaceScreens) { 249 // Lock on the app so that we don't try and get the items while apps are being added 250 LauncherAppState app = LauncherAppState.getInstance(); 251 LauncherModel model = app.getModel(); 252 boolean found = false; 253 synchronized (app) { 254 if (sWorkerThread.getThreadId() != Process.myTid()) { 255 // Flush the LauncherModel worker thread, so that if we just did another 256 // processInstallShortcut, we give it time for its shortcut to get added to the 257 // database (getItemsInLocalCoordinates reads the database) 258 model.flushWorkerThread(); 259 } 260 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context); 261 262 // Try adding to the workspace screens incrementally, starting at the default or center 263 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1)) 264 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size()); 265 int count = workspaceScreens.size(); 266 for (int screen = firstScreenIndex; screen < count && !found; screen++) { 267 int[] tmpCoordinates = new int[2]; 268 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates, 269 workspaceScreens.get(screen))) { 270 // Update the Launcher db 271 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates); 272 } 273 } 274 } 275 return null; 276 } 277 278 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> added) { 279 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 280 addAndBindAddedApps(context, added, cb); 281 } 282 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> added, 283 final Callbacks callbacks) { 284 Log.w(TAG, "10249126 - addAndBindAddedApps()"); 285 if (added.isEmpty()) { 286 return; 287 } 288 // Process the newly added applications and add them to the database first 289 Runnable r = new Runnable() { 290 public void run() { 291 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>(); 292 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>(); 293 294 // Get the list of workspace screens. We need to append to this list and 295 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been 296 // called. 297 ArrayList<Long> workspaceScreens = new ArrayList<Long>(); 298 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context); 299 for (Integer i : orderedScreens.keySet()) { 300 long screenId = orderedScreens.get(i); 301 workspaceScreens.add(screenId); 302 } 303 304 synchronized(sBgLock) { 305 Iterator<ItemInfo> iter = added.iterator(); 306 while (iter.hasNext()) { 307 ItemInfo a = iter.next(); 308 final String name = a.title.toString(); 309 final Intent launchIntent = a.getIntent(); 310 311 // Short-circuit this logic if the icon exists somewhere on the workspace 312 if (LauncherModel.shortcutExists(context, name, launchIntent)) { 313 continue; 314 } 315 316 // Add this icon to the db, creating a new page if necessary 317 int startSearchPageIndex = 1; 318 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context, 319 name, launchIntent, startSearchPageIndex, workspaceScreens); 320 if (coords == null) { 321 LauncherProvider lp = LauncherAppState.getLauncherProvider(); 322 323 // If we can't find a valid position, then just add a new screen. 324 // This takes time so we need to re-queue the add until the new 325 // page is added. Create as many screens as necessary to satisfy 326 // the startSearchPageIndex. 327 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 - 328 workspaceScreens.size()); 329 while (numPagesToAdd > 0) { 330 long screenId = lp.generateNewScreenId(); 331 Log.w(TAG, "10249126 - addAndBindAddedApps(" + screenId + ")"); 332 // Save the screen id for binding in the workspace 333 workspaceScreens.add(screenId); 334 addedWorkspaceScreensFinal.add(screenId); 335 numPagesToAdd--; 336 } 337 338 // Find the coordinate again 339 coords = LauncherModel.findNextAvailableIconSpace(context, 340 name, launchIntent, startSearchPageIndex, workspaceScreens); 341 } 342 if (coords == null) { 343 throw new RuntimeException("Coordinates should not be null"); 344 } 345 346 ShortcutInfo shortcutInfo; 347 if (a instanceof ShortcutInfo) { 348 shortcutInfo = (ShortcutInfo) a; 349 } else if (a instanceof ApplicationInfo) { 350 shortcutInfo = ((ApplicationInfo) a).makeShortcut(); 351 } else { 352 throw new RuntimeException("Unexpected info type"); 353 } 354 // Add the shortcut to the db 355 addItemToDatabase(context, shortcutInfo, 356 LauncherSettings.Favorites.CONTAINER_DESKTOP, 357 coords.first, coords.second[0], coords.second[1], false); 358 // Save the ShortcutInfo for binding in the workspace 359 addedShortcutsFinal.add(shortcutInfo); 360 } 361 } 362 363 Log.w(TAG, "10249126 - addAndBindAddedApps - updateWorkspaceScreenOrder(" + workspaceScreens.size() + ")"); 364 365 // Update the workspace screens 366 updateWorkspaceScreenOrder(context, workspaceScreens); 367 368 if (!addedShortcutsFinal.isEmpty()) { 369 runOnMainThread(new Runnable() { 370 public void run() { 371 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 372 if (callbacks == cb && cb != null) { 373 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1); 374 long lastScreenId = info.screenId; 375 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>(); 376 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>(); 377 for (ItemInfo i : addedShortcutsFinal) { 378 if (i.screenId == lastScreenId) { 379 addAnimated.add(i); 380 } else { 381 addNotAnimated.add(i); 382 } 383 } 384 callbacks.bindAppsAdded(addedWorkspaceScreensFinal, 385 addNotAnimated, addAnimated); 386 } 387 } 388 }); 389 } 390 } 391 }; 392 runOnWorkerThread(r); 393 } 394 395 public Bitmap getFallbackIcon() { 396 return Bitmap.createBitmap(mDefaultIcon); 397 } 398 399 public void unbindItemInfosAndClearQueuedBindRunnables() { 400 if (sWorkerThread.getThreadId() == Process.myTid()) { 401 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " + 402 "main thread"); 403 } 404 405 // Clear any deferred bind runnables 406 mDeferredBindRunnables.clear(); 407 // Remove any queued bind runnables 408 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE); 409 // Unbind all the workspace items 410 unbindWorkspaceItemsOnMainThread(); 411 } 412 413 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */ 414 void unbindWorkspaceItemsOnMainThread() { 415 // Ensure that we don't use the same workspace items data structure on the main thread 416 // by making a copy of workspace items first. 417 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>(); 418 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>(); 419 synchronized (sBgLock) { 420 tmpWorkspaceItems.addAll(sBgWorkspaceItems); 421 tmpAppWidgets.addAll(sBgAppWidgets); 422 } 423 Runnable r = new Runnable() { 424 @Override 425 public void run() { 426 for (ItemInfo item : tmpWorkspaceItems) { 427 item.unbind(); 428 } 429 for (ItemInfo item : tmpAppWidgets) { 430 item.unbind(); 431 } 432 } 433 }; 434 runOnMainThread(r); 435 } 436 437 /** 438 * Adds an item to the DB if it was not created previously, or move it to a new 439 * <container, screen, cellX, cellY> 440 */ 441 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container, 442 long screenId, int cellX, int cellY) { 443 if (item.container == ItemInfo.NO_ID) { 444 // From all apps 445 addItemToDatabase(context, item, container, screenId, cellX, cellY, false); 446 } else { 447 // From somewhere else 448 moveItemInDatabase(context, item, container, screenId, cellX, cellY); 449 } 450 } 451 452 static void checkItemInfoLocked( 453 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) { 454 ItemInfo modelItem = sBgItemsIdMap.get(itemId); 455 if (modelItem != null && item != modelItem) { 456 // check all the data is consistent 457 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) { 458 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem; 459 ShortcutInfo shortcut = (ShortcutInfo) item; 460 if (modelShortcut.title.toString().equals(shortcut.title.toString()) && 461 modelShortcut.intent.filterEquals(shortcut.intent) && 462 modelShortcut.id == shortcut.id && 463 modelShortcut.itemType == shortcut.itemType && 464 modelShortcut.container == shortcut.container && 465 modelShortcut.screenId == shortcut.screenId && 466 modelShortcut.cellX == shortcut.cellX && 467 modelShortcut.cellY == shortcut.cellY && 468 modelShortcut.spanX == shortcut.spanX && 469 modelShortcut.spanY == shortcut.spanY && 470 ((modelShortcut.dropPos == null && shortcut.dropPos == null) || 471 (modelShortcut.dropPos != null && 472 shortcut.dropPos != null && 473 modelShortcut.dropPos[0] == shortcut.dropPos[0] && 474 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) { 475 // For all intents and purposes, this is the same object 476 return; 477 } 478 } 479 480 // the modelItem needs to match up perfectly with item if our model is 481 // to be consistent with the database-- for now, just require 482 // modelItem == item or the equality check above 483 String msg = "item: " + ((item != null) ? item.toString() : "null") + 484 "modelItem: " + 485 ((modelItem != null) ? modelItem.toString() : "null") + 486 "Error: ItemInfo passed to checkItemInfo doesn't match original"; 487 RuntimeException e = new RuntimeException(msg); 488 if (stackTrace != null) { 489 e.setStackTrace(stackTrace); 490 } 491 // TODO: something breaks this in the upgrade path 492 //throw e; 493 } 494 } 495 496 static void checkItemInfo(final ItemInfo item) { 497 final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 498 final long itemId = item.id; 499 Runnable r = new Runnable() { 500 public void run() { 501 synchronized (sBgLock) { 502 checkItemInfoLocked(itemId, item, stackTrace); 503 } 504 } 505 }; 506 runOnWorkerThread(r); 507 } 508 509 static void updateItemInDatabaseHelper(Context context, final ContentValues values, 510 final ItemInfo item, final String callingFunction) { 511 final long itemId = item.id; 512 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false); 513 final ContentResolver cr = context.getContentResolver(); 514 515 final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 516 Runnable r = new Runnable() { 517 public void run() { 518 cr.update(uri, values, null, null); 519 updateItemArrays(item, itemId, stackTrace); 520 } 521 }; 522 runOnWorkerThread(r); 523 } 524 525 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList, 526 final ArrayList<ItemInfo> items, final String callingFunction) { 527 final ContentResolver cr = context.getContentResolver(); 528 529 final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 530 Runnable r = new Runnable() { 531 public void run() { 532 ArrayList<ContentProviderOperation> ops = 533 new ArrayList<ContentProviderOperation>(); 534 int count = items.size(); 535 for (int i = 0; i < count; i++) { 536 ItemInfo item = items.get(i); 537 final long itemId = item.id; 538 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false); 539 ContentValues values = valuesList.get(i); 540 541 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); 542 updateItemArrays(item, itemId, stackTrace); 543 544 } 545 try { 546 cr.applyBatch(LauncherProvider.AUTHORITY, ops); 547 } catch (Exception e) { 548 e.printStackTrace(); 549 } 550 } 551 }; 552 runOnWorkerThread(r); 553 } 554 555 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) { 556 // Lock on mBgLock *after* the db operation 557 synchronized (sBgLock) { 558 checkItemInfoLocked(itemId, item, stackTrace); 559 560 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP && 561 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 562 // Item is in a folder, make sure this folder exists 563 if (!sBgFolders.containsKey(item.container)) { 564 // An items container is being set to a that of an item which is not in 565 // the list of Folders. 566 String msg = "item: " + item + " container being set to: " + 567 item.container + ", not in the list of folders"; 568 Log.e(TAG, msg); 569 Launcher.dumpDebugLogsToConsole(); 570 } 571 } 572 573 // Items are added/removed from the corresponding FolderInfo elsewhere, such 574 // as in Workspace.onDrop. Here, we just add/remove them from the list of items 575 // that are on the desktop, as appropriate 576 ItemInfo modelItem = sBgItemsIdMap.get(itemId); 577 if (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || 578 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 579 switch (modelItem.itemType) { 580 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 581 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 582 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 583 if (!sBgWorkspaceItems.contains(modelItem)) { 584 sBgWorkspaceItems.add(modelItem); 585 } 586 break; 587 default: 588 break; 589 } 590 } else { 591 sBgWorkspaceItems.remove(modelItem); 592 } 593 } 594 } 595 596 public void flushWorkerThread() { 597 mFlushingWorkerThread = true; 598 Runnable waiter = new Runnable() { 599 public void run() { 600 synchronized (this) { 601 notifyAll(); 602 mFlushingWorkerThread = false; 603 } 604 } 605 }; 606 607 synchronized(waiter) { 608 runOnWorkerThread(waiter); 609 if (mLoaderTask != null) { 610 synchronized(mLoaderTask) { 611 mLoaderTask.notify(); 612 } 613 } 614 boolean success = false; 615 while (!success) { 616 try { 617 waiter.wait(); 618 success = true; 619 } catch (InterruptedException e) { 620 } 621 } 622 } 623 } 624 625 /** 626 * Move an item in the DB to a new <container, screen, cellX, cellY> 627 */ 628 static void moveItemInDatabase(Context context, final ItemInfo item, final long container, 629 final long screenId, final int cellX, final int cellY) { 630 String transaction = "DbDebug Modify item (" + item.title + ") in db, id: " + item.id + 631 " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY + 632 ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")"; 633 Launcher.sDumpLogs.add(transaction); 634 Log.d(TAG, transaction); 635 item.container = container; 636 item.cellX = cellX; 637 item.cellY = cellY; 638 639 // We store hotseat items in canonical form which is this orientation invariant position 640 // in the hotseat 641 if (context instanceof Launcher && screenId < 0 && 642 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 643 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY); 644 } else { 645 item.screenId = screenId; 646 } 647 648 final ContentValues values = new ContentValues(); 649 values.put(LauncherSettings.Favorites.CONTAINER, item.container); 650 values.put(LauncherSettings.Favorites.CELLX, item.cellX); 651 values.put(LauncherSettings.Favorites.CELLY, item.cellY); 652 values.put(LauncherSettings.Favorites.SCREEN, item.screenId); 653 654 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase"); 655 } 656 657 /** 658 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the 659 * cellX, cellY have already been updated on the ItemInfos. 660 */ 661 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items, 662 final long container, final int screen) { 663 664 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>(); 665 int count = items.size(); 666 667 for (int i = 0; i < count; i++) { 668 ItemInfo item = items.get(i); 669 String transaction = "DbDebug Modify item (" + item.title + ") in db, id: " 670 + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX 671 + ", " + item.cellY + ") --> " + "(" + container + ", " + screen + ", " 672 + item.cellX + ", " + item.cellY + ")"; 673 Launcher.sDumpLogs.add(transaction); 674 item.container = container; 675 676 // We store hotseat items in canonical form which is this orientation invariant position 677 // in the hotseat 678 if (context instanceof Launcher && screen < 0 && 679 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 680 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX, 681 item.cellY); 682 } else { 683 item.screenId = screen; 684 } 685 686 final ContentValues values = new ContentValues(); 687 values.put(LauncherSettings.Favorites.CONTAINER, item.container); 688 values.put(LauncherSettings.Favorites.CELLX, item.cellX); 689 values.put(LauncherSettings.Favorites.CELLY, item.cellY); 690 values.put(LauncherSettings.Favorites.SCREEN, item.screenId); 691 692 contentValues.add(values); 693 } 694 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase"); 695 } 696 697 /** 698 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY> 699 */ 700 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container, 701 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) { 702 String transaction = "DbDebug Modify item (" + item.title + ") in db, id: " + item.id + 703 " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY + 704 ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")"; 705 Launcher.sDumpLogs.add(transaction); 706 Log.d(TAG, transaction); 707 item.cellX = cellX; 708 item.cellY = cellY; 709 item.spanX = spanX; 710 item.spanY = spanY; 711 712 // We store hotseat items in canonical form which is this orientation invariant position 713 // in the hotseat 714 if (context instanceof Launcher && screenId < 0 && 715 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 716 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY); 717 } else { 718 item.screenId = screenId; 719 } 720 721 final ContentValues values = new ContentValues(); 722 values.put(LauncherSettings.Favorites.CONTAINER, item.container); 723 values.put(LauncherSettings.Favorites.CELLX, item.cellX); 724 values.put(LauncherSettings.Favorites.CELLY, item.cellY); 725 values.put(LauncherSettings.Favorites.SPANX, item.spanX); 726 values.put(LauncherSettings.Favorites.SPANY, item.spanY); 727 values.put(LauncherSettings.Favorites.SCREEN, item.screenId); 728 729 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase"); 730 } 731 732 /** 733 * Update an item to the database in a specified container. 734 */ 735 static void updateItemInDatabase(Context context, final ItemInfo item) { 736 final ContentValues values = new ContentValues(); 737 item.onAddToDatabase(values); 738 item.updateValuesWithCoordinates(values, item.cellX, item.cellY); 739 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase"); 740 } 741 742 /** 743 * Returns true if the shortcuts already exists in the database. 744 * we identify a shortcut by its title and intent. 745 */ 746 static boolean shortcutExists(Context context, String title, Intent intent) { 747 final ContentResolver cr = context.getContentResolver(); 748 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, 749 new String[] { "title", "intent" }, "title=? and intent=?", 750 new String[] { title, intent.toUri(0) }, null); 751 boolean result = false; 752 try { 753 result = c.moveToFirst(); 754 } finally { 755 c.close(); 756 } 757 return result; 758 } 759 760 /** 761 * Returns an ItemInfo array containing all the items in the LauncherModel. 762 * The ItemInfo.id is not set through this function. 763 */ 764 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) { 765 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>(); 766 final ContentResolver cr = context.getContentResolver(); 767 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] { 768 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER, 769 LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, 770 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null); 771 772 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 773 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); 774 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); 775 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); 776 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); 777 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); 778 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); 779 780 try { 781 while (c.moveToNext()) { 782 ItemInfo item = new ItemInfo(); 783 item.cellX = c.getInt(cellXIndex); 784 item.cellY = c.getInt(cellYIndex); 785 item.spanX = Math.max(1, c.getInt(spanXIndex)); 786 item.spanY = Math.max(1, c.getInt(spanYIndex)); 787 item.container = c.getInt(containerIndex); 788 item.itemType = c.getInt(itemTypeIndex); 789 item.screenId = c.getInt(screenIndex); 790 791 items.add(item); 792 } 793 } catch (Exception e) { 794 items.clear(); 795 } finally { 796 c.close(); 797 } 798 799 return items; 800 } 801 802 /** 803 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList. 804 */ 805 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) { 806 final ContentResolver cr = context.getContentResolver(); 807 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null, 808 "_id=? and (itemType=? or itemType=?)", 809 new String[] { String.valueOf(id), 810 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null); 811 812 try { 813 if (c.moveToFirst()) { 814 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 815 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); 816 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); 817 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); 818 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); 819 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); 820 821 FolderInfo folderInfo = null; 822 switch (c.getInt(itemTypeIndex)) { 823 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 824 folderInfo = findOrMakeFolder(folderList, id); 825 break; 826 } 827 828 folderInfo.title = c.getString(titleIndex); 829 folderInfo.id = id; 830 folderInfo.container = c.getInt(containerIndex); 831 folderInfo.screenId = c.getInt(screenIndex); 832 folderInfo.cellX = c.getInt(cellXIndex); 833 folderInfo.cellY = c.getInt(cellYIndex); 834 835 return folderInfo; 836 } 837 } finally { 838 c.close(); 839 } 840 841 return null; 842 } 843 844 /** 845 * Add an item to the database in a specified container. Sets the container, screen, cellX and 846 * cellY fields of the item. Also assigns an ID to the item. 847 */ 848 static void addItemToDatabase(Context context, final ItemInfo item, final long container, 849 final long screenId, final int cellX, final int cellY, final boolean notify) { 850 item.container = container; 851 item.cellX = cellX; 852 item.cellY = cellY; 853 // We store hotseat items in canonical form which is this orientation invariant position 854 // in the hotseat 855 if (context instanceof Launcher && screenId < 0 && 856 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 857 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY); 858 } else { 859 item.screenId = screenId; 860 } 861 862 final ContentValues values = new ContentValues(); 863 final ContentResolver cr = context.getContentResolver(); 864 item.onAddToDatabase(values); 865 866 item.id = LauncherAppState.getLauncherProvider().generateNewItemId(); 867 values.put(LauncherSettings.Favorites._ID, item.id); 868 item.updateValuesWithCoordinates(values, item.cellX, item.cellY); 869 870 Runnable r = new Runnable() { 871 public void run() { 872 String transaction = "DbDebug Add item (" + item.title + ") to db, id: " 873 + item.id + " (" + container + ", " + screenId + ", " + cellX + ", " 874 + cellY + ")"; 875 Launcher.sDumpLogs.add(transaction); 876 Log.d(TAG, transaction); 877 878 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI : 879 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values); 880 881 // Lock on mBgLock *after* the db operation 882 synchronized (sBgLock) { 883 checkItemInfoLocked(item.id, item, null); 884 sBgItemsIdMap.put(item.id, item); 885 switch (item.itemType) { 886 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 887 sBgFolders.put(item.id, (FolderInfo) item); 888 // Fall through 889 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 890 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 891 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || 892 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 893 sBgWorkspaceItems.add(item); 894 } else { 895 if (!sBgFolders.containsKey(item.container)) { 896 // Adding an item to a folder that doesn't exist. 897 String msg = "adding item: " + item + " to a folder that " + 898 " doesn't exist"; 899 Log.e(TAG, msg); 900 Launcher.dumpDebugLogsToConsole(); 901 } 902 } 903 break; 904 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 905 sBgAppWidgets.add((LauncherAppWidgetInfo) item); 906 break; 907 } 908 } 909 } 910 }; 911 runOnWorkerThread(r); 912 } 913 914 /** 915 * Creates a new unique child id, for a given cell span across all layouts. 916 */ 917 static int getCellLayoutChildId( 918 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) { 919 return (((int) container & 0xFF) << 24) 920 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF); 921 } 922 923 /** 924 * Removes the specified item from the database 925 * @param context 926 * @param item 927 */ 928 static void deleteItemFromDatabase(Context context, final ItemInfo item) { 929 final ContentResolver cr = context.getContentResolver(); 930 final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false); 931 932 Runnable r = new Runnable() { 933 public void run() { 934 String transaction = "DbDebug Delete item (" + item.title + ") from db, id: " 935 + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX + 936 ", " + item.cellY + ")"; 937 Launcher.sDumpLogs.add(transaction); 938 Log.d(TAG, transaction); 939 940 cr.delete(uriToDelete, null, null); 941 942 // Lock on mBgLock *after* the db operation 943 synchronized (sBgLock) { 944 switch (item.itemType) { 945 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 946 sBgFolders.remove(item.id); 947 for (ItemInfo info: sBgItemsIdMap.values()) { 948 if (info.container == item.id) { 949 // We are deleting a folder which still contains items that 950 // think they are contained by that folder. 951 String msg = "deleting a folder (" + item + ") which still " + 952 "contains items (" + info + ")"; 953 Log.e(TAG, msg); 954 Launcher.dumpDebugLogsToConsole(); 955 } 956 } 957 sBgWorkspaceItems.remove(item); 958 break; 959 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 960 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 961 sBgWorkspaceItems.remove(item); 962 break; 963 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 964 sBgAppWidgets.remove((LauncherAppWidgetInfo) item); 965 break; 966 } 967 sBgItemsIdMap.remove(item.id); 968 sBgDbIconCache.remove(item); 969 } 970 } 971 }; 972 runOnWorkerThread(r); 973 } 974 975 /** 976 * Update the order of the workspace screens in the database. The array list contains 977 * a list of screen ids in the order that they should appear. 978 */ 979 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) { 980 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder()"); 981 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens); 982 final ContentResolver cr = context.getContentResolver(); 983 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; 984 985 // Remove any negative screen ids -- these aren't persisted 986 Iterator<Long> iter = screensCopy.iterator(); 987 while (iter.hasNext()) { 988 long id = iter.next(); 989 if (id < 0) { 990 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - remove: " + id + ")"); 991 iter.remove(); 992 } 993 } 994 995 // Dump the screens copy 996 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - screensCopy"); 997 for (Long l : screensCopy) { 998 Log.w(TAG, "10249126\t- " + l); 999 } 1000 1001 Runnable r = new Runnable() { 1002 @Override 1003 public void run() { 1004 // Clear the table 1005 cr.delete(uri, null, null); 1006 int count = screensCopy.size(); 1007 ContentValues[] values = new ContentValues[count]; 1008 for (int i = 0; i < count; i++) { 1009 ContentValues v = new ContentValues(); 1010 long screenId = screensCopy.get(i); 1011 v.put(LauncherSettings.WorkspaceScreens._ID, screenId); 1012 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); 1013 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - add: " + 1014 screenId + ", " + i + ")"); 1015 values[i] = v; 1016 } 1017 cr.bulkInsert(uri, values); 1018 1019 // Dump the sBgWorkspaceScreens 1020 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens"); 1021 for (Long l : sBgWorkspaceScreens) { 1022 Log.w(TAG, "10249126\t- " + l); 1023 } 1024 1025 sBgWorkspaceScreens.clear(); 1026 sBgWorkspaceScreens.addAll(screensCopy); 1027 } 1028 }; 1029 runOnWorkerThread(r); 1030 } 1031 1032 /** 1033 * Remove the contents of the specified folder from the database 1034 */ 1035 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) { 1036 final ContentResolver cr = context.getContentResolver(); 1037 1038 Runnable r = new Runnable() { 1039 public void run() { 1040 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null); 1041 // Lock on mBgLock *after* the db operation 1042 synchronized (sBgLock) { 1043 sBgItemsIdMap.remove(info.id); 1044 sBgFolders.remove(info.id); 1045 sBgDbIconCache.remove(info); 1046 sBgWorkspaceItems.remove(info); 1047 } 1048 1049 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, 1050 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null); 1051 // Lock on mBgLock *after* the db operation 1052 synchronized (sBgLock) { 1053 for (ItemInfo childInfo : info.contents) { 1054 sBgItemsIdMap.remove(childInfo.id); 1055 sBgDbIconCache.remove(childInfo); 1056 } 1057 } 1058 } 1059 }; 1060 runOnWorkerThread(r); 1061 } 1062 1063 /** 1064 * Set this as the current Launcher activity object for the loader. 1065 */ 1066 public void initialize(Callbacks callbacks) { 1067 synchronized (mLock) { 1068 mCallbacks = new WeakReference<Callbacks>(callbacks); 1069 } 1070 } 1071 1072 /** 1073 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and 1074 * ACTION_PACKAGE_CHANGED. 1075 */ 1076 @Override 1077 public void onReceive(Context context, Intent intent) { 1078 if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent); 1079 1080 final String action = intent.getAction(); 1081 1082 if (Intent.ACTION_PACKAGE_CHANGED.equals(action) 1083 || Intent.ACTION_PACKAGE_REMOVED.equals(action) 1084 || Intent.ACTION_PACKAGE_ADDED.equals(action)) { 1085 final String packageName = intent.getData().getSchemeSpecificPart(); 1086 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 1087 1088 int op = PackageUpdatedTask.OP_NONE; 1089 1090 if (packageName == null || packageName.length() == 0) { 1091 // they sent us a bad intent 1092 return; 1093 } 1094 1095 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 1096 op = PackageUpdatedTask.OP_UPDATE; 1097 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 1098 if (!replacing) { 1099 op = PackageUpdatedTask.OP_REMOVE; 1100 } 1101 // else, we are replacing the package, so a PACKAGE_ADDED will be sent 1102 // later, we will update the package at this time 1103 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { 1104 if (!replacing) { 1105 op = PackageUpdatedTask.OP_ADD; 1106 } else { 1107 op = PackageUpdatedTask.OP_UPDATE; 1108 } 1109 } 1110 1111 if (op != PackageUpdatedTask.OP_NONE) { 1112 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName })); 1113 } 1114 1115 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1116 // First, schedule to add these apps back in. 1117 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1118 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); 1119 // Then, rebind everything. 1120 startLoaderFromBackground(); 1121 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 1122 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1123 enqueuePackageUpdated(new PackageUpdatedTask( 1124 PackageUpdatedTask.OP_UNAVAILABLE, packages)); 1125 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 1126 // If we have changed locale we need to clear out the labels in all apps/workspace. 1127 forceReload(); 1128 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 1129 // Check if configuration change was an mcc/mnc change which would affect app resources 1130 // and we would need to clear out the labels in all apps/workspace. Same handling as 1131 // above for ACTION_LOCALE_CHANGED 1132 Configuration currentConfig = context.getResources().getConfiguration(); 1133 if (mPreviousConfigMcc != currentConfig.mcc) { 1134 Log.d(TAG, "Reload apps on config change. curr_mcc:" 1135 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc); 1136 forceReload(); 1137 } 1138 // Update previousConfig 1139 mPreviousConfigMcc = currentConfig.mcc; 1140 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) || 1141 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) { 1142 if (mCallbacks != null) { 1143 Callbacks callbacks = mCallbacks.get(); 1144 if (callbacks != null) { 1145 callbacks.bindSearchablesChanged(); 1146 } 1147 } 1148 } 1149 } 1150 1151 private void forceReload() { 1152 resetLoadedState(true, true); 1153 1154 // Do this here because if the launcher activity is running it will be restarted. 1155 // If it's not running startLoaderFromBackground will merely tell it that it needs 1156 // to reload. 1157 startLoaderFromBackground(); 1158 } 1159 1160 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) { 1161 synchronized (mLock) { 1162 // Stop any existing loaders first, so they don't set mAllAppsLoaded or 1163 // mWorkspaceLoaded to true later 1164 stopLoaderLocked(); 1165 if (resetAllAppsLoaded) mAllAppsLoaded = false; 1166 if (resetWorkspaceLoaded) mWorkspaceLoaded = false; 1167 } 1168 } 1169 1170 /** 1171 * When the launcher is in the background, it's possible for it to miss paired 1172 * configuration changes. So whenever we trigger the loader from the background 1173 * tell the launcher that it needs to re-run the loader when it comes back instead 1174 * of doing it now. 1175 */ 1176 public void startLoaderFromBackground() { 1177 boolean runLoader = false; 1178 if (mCallbacks != null) { 1179 Callbacks callbacks = mCallbacks.get(); 1180 if (callbacks != null) { 1181 // Only actually run the loader if they're not paused. 1182 if (!callbacks.setLoadOnResume()) { 1183 runLoader = true; 1184 } 1185 } 1186 } 1187 if (runLoader) { 1188 startLoader(false, -1); 1189 } 1190 } 1191 1192 // If there is already a loader task running, tell it to stop. 1193 // returns true if isLaunching() was true on the old task 1194 private boolean stopLoaderLocked() { 1195 boolean isLaunching = false; 1196 LoaderTask oldTask = mLoaderTask; 1197 if (oldTask != null) { 1198 if (oldTask.isLaunching()) { 1199 isLaunching = true; 1200 } 1201 oldTask.stopLocked(); 1202 } 1203 return isLaunching; 1204 } 1205 1206 public void startLoader(boolean isLaunching, int synchronousBindPage) { 1207 synchronized (mLock) { 1208 if (DEBUG_LOADERS) { 1209 Log.d(TAG, "startLoader isLaunching=" + isLaunching); 1210 } 1211 1212 // Clear any deferred bind-runnables from the synchronized load process 1213 // We must do this before any loading/binding is scheduled below. 1214 mDeferredBindRunnables.clear(); 1215 1216 // Don't bother to start the thread if we know it's not going to do anything 1217 if (mCallbacks != null && mCallbacks.get() != null) { 1218 // If there is already one running, tell it to stop. 1219 // also, don't downgrade isLaunching if we're already running 1220 isLaunching = isLaunching || stopLoaderLocked(); 1221 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching); 1222 if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { 1223 mLoaderTask.runBindSynchronousPage(synchronousBindPage); 1224 } else { 1225 sWorkerThread.setPriority(Thread.NORM_PRIORITY); 1226 sWorker.post(mLoaderTask); 1227 } 1228 } 1229 } 1230 } 1231 1232 void bindRemainingSynchronousPages() { 1233 // Post the remaining side pages to be loaded 1234 if (!mDeferredBindRunnables.isEmpty()) { 1235 for (final Runnable r : mDeferredBindRunnables) { 1236 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE); 1237 } 1238 mDeferredBindRunnables.clear(); 1239 } 1240 } 1241 1242 public void stopLoader() { 1243 synchronized (mLock) { 1244 if (mLoaderTask != null) { 1245 mLoaderTask.stopLocked(); 1246 } 1247 } 1248 } 1249 1250 /** Loads the workspace screens db into a map of Rank -> ScreenId */ 1251 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) { 1252 final ContentResolver contentResolver = context.getContentResolver(); 1253 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; 1254 final Cursor sc = contentResolver.query(screensUri, null, null, null, null); 1255 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>(); 1256 1257 try { 1258 final int idIndex = sc.getColumnIndexOrThrow( 1259 LauncherSettings.WorkspaceScreens._ID); 1260 final int rankIndex = sc.getColumnIndexOrThrow( 1261 LauncherSettings.WorkspaceScreens.SCREEN_RANK); 1262 while (sc.moveToNext()) { 1263 try { 1264 long screenId = sc.getLong(idIndex); 1265 int rank = sc.getInt(rankIndex); 1266 1267 Log.w(TAG, "10249126 - loadWorkspaceScreensDb(" + screenId + ", " + rank + ")"); 1268 1269 orderedScreens.put(rank, screenId); 1270 } catch (Exception e) { 1271 Log.w(TAG, "Desktop items loading interrupted - invalid screens: ", e); 1272 } 1273 } 1274 } finally { 1275 sc.close(); 1276 } 1277 return orderedScreens; 1278 } 1279 1280 public boolean isAllAppsLoaded() { 1281 return mAllAppsLoaded; 1282 } 1283 1284 boolean isLoadingWorkspace() { 1285 synchronized (mLock) { 1286 if (mLoaderTask != null) { 1287 return mLoaderTask.isLoadingWorkspace(); 1288 } 1289 } 1290 return false; 1291 } 1292 1293 /** 1294 * Runnable for the thread that loads the contents of the launcher: 1295 * - workspace icons 1296 * - widgets 1297 * - all apps icons 1298 */ 1299 private class LoaderTask implements Runnable { 1300 private Context mContext; 1301 private boolean mIsLaunching; 1302 private boolean mIsLoadingAndBindingWorkspace; 1303 private boolean mStopped; 1304 private boolean mLoadAndBindStepFinished; 1305 1306 private HashMap<Object, CharSequence> mLabelCache; 1307 1308 LoaderTask(Context context, boolean isLaunching) { 1309 mContext = context; 1310 mIsLaunching = isLaunching; 1311 mLabelCache = new HashMap<Object, CharSequence>(); 1312 } 1313 1314 boolean isLaunching() { 1315 return mIsLaunching; 1316 } 1317 1318 boolean isLoadingWorkspace() { 1319 return mIsLoadingAndBindingWorkspace; 1320 } 1321 1322 /** Returns whether this is an upgrade path */ 1323 private boolean loadAndBindWorkspace() { 1324 mIsLoadingAndBindingWorkspace = true; 1325 1326 // Load the workspace 1327 if (DEBUG_LOADERS) { 1328 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); 1329 } 1330 1331 boolean isUpgradePath = false; 1332 if (!mWorkspaceLoaded) { 1333 isUpgradePath = loadWorkspace(); 1334 synchronized (LoaderTask.this) { 1335 if (mStopped) { 1336 return isUpgradePath; 1337 } 1338 mWorkspaceLoaded = true; 1339 } 1340 } 1341 1342 // Bind the workspace 1343 bindWorkspace(-1, isUpgradePath); 1344 return isUpgradePath; 1345 } 1346 1347 private void waitForIdle() { 1348 // Wait until the either we're stopped or the other threads are done. 1349 // This way we don't start loading all apps until the workspace has settled 1350 // down. 1351 synchronized (LoaderTask.this) { 1352 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 1353 1354 mHandler.postIdle(new Runnable() { 1355 public void run() { 1356 synchronized (LoaderTask.this) { 1357 mLoadAndBindStepFinished = true; 1358 if (DEBUG_LOADERS) { 1359 Log.d(TAG, "done with previous binding step"); 1360 } 1361 LoaderTask.this.notify(); 1362 } 1363 } 1364 }); 1365 1366 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) { 1367 try { 1368 // Just in case mFlushingWorkerThread changes but we aren't woken up, 1369 // wait no longer than 1sec at a time 1370 this.wait(1000); 1371 } catch (InterruptedException ex) { 1372 // Ignore 1373 } 1374 } 1375 if (DEBUG_LOADERS) { 1376 Log.d(TAG, "waited " 1377 + (SystemClock.uptimeMillis()-workspaceWaitTime) 1378 + "ms for previous step to finish binding"); 1379 } 1380 } 1381 } 1382 1383 void runBindSynchronousPage(int synchronousBindPage) { 1384 if (synchronousBindPage < 0) { 1385 // Ensure that we have a valid page index to load synchronously 1386 throw new RuntimeException("Should not call runBindSynchronousPage() without " + 1387 "valid page index"); 1388 } 1389 if (!mAllAppsLoaded || !mWorkspaceLoaded) { 1390 // Ensure that we don't try and bind a specified page when the pages have not been 1391 // loaded already (we should load everything asynchronously in that case) 1392 throw new RuntimeException("Expecting AllApps and Workspace to be loaded"); 1393 } 1394 synchronized (mLock) { 1395 if (mIsLoaderTaskRunning) { 1396 // Ensure that we are never running the background loading at this point since 1397 // we also touch the background collections 1398 throw new RuntimeException("Error! Background loading is already running"); 1399 } 1400 } 1401 1402 // XXX: Throw an exception if we are already loading (since we touch the worker thread 1403 // data structures, we can't allow any other thread to touch that data, but because 1404 // this call is synchronous, we can get away with not locking). 1405 1406 // The LauncherModel is static in the LauncherAppState and mHandler may have queued 1407 // operations from the previous activity. We need to ensure that all queued operations 1408 // are executed before any synchronous binding work is done. 1409 mHandler.flush(); 1410 1411 // Divide the set of loaded items into those that we are binding synchronously, and 1412 // everything else that is to be bound normally (asynchronously). 1413 bindWorkspace(synchronousBindPage, false); 1414 // XXX: For now, continue posting the binding of AllApps as there are other issues that 1415 // arise from that. 1416 onlyBindAllApps(); 1417 } 1418 1419 public void run() { 1420 boolean isUpgrade = false; 1421 1422 synchronized (mLock) { 1423 mIsLoaderTaskRunning = true; 1424 } 1425 // Optimize for end-user experience: if the Launcher is up and // running with the 1426 // All Apps interface in the foreground, load All Apps first. Otherwise, load the 1427 // workspace first (default). 1428 keep_running: { 1429 // Elevate priority when Home launches for the first time to avoid 1430 // starving at boot time. Staring at a blank home is not cool. 1431 synchronized (mLock) { 1432 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + 1433 (mIsLaunching ? "DEFAULT" : "BACKGROUND")); 1434 android.os.Process.setThreadPriority(mIsLaunching 1435 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); 1436 } 1437 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); 1438 isUpgrade = loadAndBindWorkspace(); 1439 1440 if (mStopped) { 1441 break keep_running; 1442 } 1443 1444 // Whew! Hard work done. Slow us down, and wait until the UI thread has 1445 // settled down. 1446 synchronized (mLock) { 1447 if (mIsLaunching) { 1448 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); 1449 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 1450 } 1451 } 1452 waitForIdle(); 1453 1454 // second step 1455 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); 1456 loadAndBindAllApps(); 1457 1458 // Restore the default thread priority after we are done loading items 1459 synchronized (mLock) { 1460 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 1461 } 1462 } 1463 1464 // Update the saved icons if necessary 1465 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); 1466 synchronized (sBgLock) { 1467 for (Object key : sBgDbIconCache.keySet()) { 1468 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); 1469 } 1470 sBgDbIconCache.clear(); 1471 } 1472 1473 // Ensure that all the applications that are in the system are represented on the home 1474 // screen. 1475 Log.w(TAG, "10249126 - verifyApplications - useMoreApps=" 1476 + UPGRADE_USE_MORE_APPS_FOLDER + " isUpgrade=" + isUpgrade); 1477 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) { 1478 verifyApplications(); 1479 } 1480 1481 // Clear out this reference, otherwise we end up holding it until all of the 1482 // callback runnables are done. 1483 mContext = null; 1484 1485 synchronized (mLock) { 1486 // If we are still the last one to be scheduled, remove ourselves. 1487 if (mLoaderTask == this) { 1488 mLoaderTask = null; 1489 } 1490 mIsLoaderTaskRunning = false; 1491 } 1492 } 1493 1494 public void stopLocked() { 1495 synchronized (LoaderTask.this) { 1496 mStopped = true; 1497 this.notify(); 1498 } 1499 } 1500 1501 /** 1502 * Gets the callbacks object. If we've been stopped, or if the launcher object 1503 * has somehow been garbage collected, return null instead. Pass in the Callbacks 1504 * object that was around when the deferred message was scheduled, and if there's 1505 * a new Callbacks object around then also return null. This will save us from 1506 * calling onto it with data that will be ignored. 1507 */ 1508 Callbacks tryGetCallbacks(Callbacks oldCallbacks) { 1509 synchronized (mLock) { 1510 if (mStopped) { 1511 return null; 1512 } 1513 1514 if (mCallbacks == null) { 1515 return null; 1516 } 1517 1518 final Callbacks callbacks = mCallbacks.get(); 1519 if (callbacks != oldCallbacks) { 1520 return null; 1521 } 1522 if (callbacks == null) { 1523 Log.w(TAG, "no mCallbacks"); 1524 return null; 1525 } 1526 1527 return callbacks; 1528 } 1529 } 1530 1531 private void verifyApplications() { 1532 final Context context = mApp.getContext(); 1533 1534 // Cross reference all the applications in our apps list with items in the workspace 1535 ArrayList<ItemInfo> tmpInfos; 1536 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>(); 1537 synchronized (sBgLock) { 1538 for (ApplicationInfo app : mBgAllAppsList.data) { 1539 tmpInfos = getItemInfoForComponentName(app.componentName); 1540 Log.w(TAG, "10249126 - \t" + app.componentName.getPackageName() + ", " + tmpInfos.isEmpty()); 1541 if (tmpInfos.isEmpty()) { 1542 // We are missing an application icon, so add this to the workspace 1543 added.add(app); 1544 // This is a rare event, so lets log it 1545 Log.e(TAG, "Missing Application on load: " + app); 1546 } 1547 } 1548 } 1549 if (!added.isEmpty()) { 1550 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 1551 addAndBindAddedApps(context, added, cb); 1552 } 1553 } 1554 1555 private boolean checkItemDimensions(ItemInfo info) { 1556 LauncherAppState app = LauncherAppState.getInstance(); 1557 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); 1558 return (info.cellX + info.spanX) > (int) grid.numColumns || 1559 (info.cellY + info.spanY) > (int) grid.numRows; 1560 } 1561 1562 // check & update map of what's occupied; used to discard overlapping/invalid items 1563 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item) { 1564 LauncherAppState app = LauncherAppState.getInstance(); 1565 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); 1566 int countX = (int) grid.numColumns; 1567 int countY = (int) grid.numRows; 1568 1569 long containerIndex = item.screenId; 1570 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 1571 if (occupied.containsKey(LauncherSettings.Favorites.CONTAINER_HOTSEAT)) { 1572 if (occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT) 1573 [(int) item.screenId][0] != null) { 1574 Log.e(TAG, "Error loading shortcut into hotseat " + item 1575 + " into position (" + item.screenId + ":" + item.cellX + "," 1576 + item.cellY + ") occupied by " 1577 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT) 1578 [(int) item.screenId][0]); 1579 return false; 1580 } 1581 } else { 1582 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1]; 1583 items[(int) item.screenId][0] = item; 1584 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items); 1585 return true; 1586 } 1587 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { 1588 // Skip further checking if it is not the hotseat or workspace container 1589 return true; 1590 } 1591 1592 if (!occupied.containsKey(item.screenId)) { 1593 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1]; 1594 occupied.put(item.screenId, items); 1595 } 1596 1597 ItemInfo[][] screens = occupied.get(item.screenId); 1598 // Check if any workspace icons overlap with each other 1599 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { 1600 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { 1601 if (screens[x][y] != null) { 1602 Log.e(TAG, "Error loading shortcut " + item 1603 + " into cell (" + containerIndex + "-" + item.screenId + ":" 1604 + x + "," + y 1605 + ") occupied by " 1606 + screens[x][y]); 1607 return false; 1608 } 1609 } 1610 } 1611 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { 1612 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { 1613 screens[x][y] = item; 1614 } 1615 } 1616 1617 return true; 1618 } 1619 1620 /** Returns whether this is an upgradge path */ 1621 private boolean loadWorkspace() { 1622 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 1623 1624 final Context context = mContext; 1625 final ContentResolver contentResolver = context.getContentResolver(); 1626 final PackageManager manager = context.getPackageManager(); 1627 final AppWidgetManager widgets = AppWidgetManager.getInstance(context); 1628 final boolean isSafeMode = manager.isSafeMode(); 1629 1630 LauncherAppState app = LauncherAppState.getInstance(); 1631 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); 1632 int countX = (int) grid.numColumns; 1633 int countY = (int) grid.numRows; 1634 1635 // Make sure the default workspace is loaded, if needed 1636 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0); 1637 1638 // Check if we need to do any upgrade-path logic 1639 boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb(); 1640 1641 synchronized (sBgLock) { 1642 sBgWorkspaceItems.clear(); 1643 sBgAppWidgets.clear(); 1644 sBgFolders.clear(); 1645 sBgItemsIdMap.clear(); 1646 sBgDbIconCache.clear(); 1647 sBgWorkspaceScreens.clear(); 1648 Log.w(TAG, "10249126 - loadWorkspace()"); 1649 1650 final ArrayList<Long> itemsToRemove = new ArrayList<Long>(); 1651 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI; 1652 final Cursor c = contentResolver.query(contentUri, null, null, null, null); 1653 1654 // +1 for the hotseat (it can be larger than the workspace) 1655 // Load workspace in reverse order to ensure that latest items are loaded first (and 1656 // before any earlier duplicates) 1657 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>(); 1658 1659 try { 1660 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); 1661 final int intentIndex = c.getColumnIndexOrThrow 1662 (LauncherSettings.Favorites.INTENT); 1663 final int titleIndex = c.getColumnIndexOrThrow 1664 (LauncherSettings.Favorites.TITLE); 1665 final int iconTypeIndex = c.getColumnIndexOrThrow( 1666 LauncherSettings.Favorites.ICON_TYPE); 1667 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); 1668 final int iconPackageIndex = c.getColumnIndexOrThrow( 1669 LauncherSettings.Favorites.ICON_PACKAGE); 1670 final int iconResourceIndex = c.getColumnIndexOrThrow( 1671 LauncherSettings.Favorites.ICON_RESOURCE); 1672 final int containerIndex = c.getColumnIndexOrThrow( 1673 LauncherSettings.Favorites.CONTAINER); 1674 final int itemTypeIndex = c.getColumnIndexOrThrow( 1675 LauncherSettings.Favorites.ITEM_TYPE); 1676 final int appWidgetIdIndex = c.getColumnIndexOrThrow( 1677 LauncherSettings.Favorites.APPWIDGET_ID); 1678 final int screenIndex = c.getColumnIndexOrThrow( 1679 LauncherSettings.Favorites.SCREEN); 1680 final int cellXIndex = c.getColumnIndexOrThrow 1681 (LauncherSettings.Favorites.CELLX); 1682 final int cellYIndex = c.getColumnIndexOrThrow 1683 (LauncherSettings.Favorites.CELLY); 1684 final int spanXIndex = c.getColumnIndexOrThrow 1685 (LauncherSettings.Favorites.SPANX); 1686 final int spanYIndex = c.getColumnIndexOrThrow( 1687 LauncherSettings.Favorites.SPANY); 1688 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); 1689 //final int displayModeIndex = c.getColumnIndexOrThrow( 1690 // LauncherSettings.Favorites.DISPLAY_MODE); 1691 1692 ShortcutInfo info; 1693 String intentDescription; 1694 LauncherAppWidgetInfo appWidgetInfo; 1695 int container; 1696 long id; 1697 Intent intent; 1698 1699 while (!mStopped && c.moveToNext()) { 1700 try { 1701 int itemType = c.getInt(itemTypeIndex); 1702 1703 switch (itemType) { 1704 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 1705 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 1706 id = c.getLong(idIndex); 1707 intentDescription = c.getString(intentIndex); 1708 try { 1709 intent = Intent.parseUri(intentDescription, 0); 1710 ComponentName cn = intent.getComponent(); 1711 if (!isValidPackageComponent(manager, cn)) { 1712 if (!mAppsCanBeOnRemoveableStorage) { 1713 // Log the invalid package, and remove it from the db 1714 Uri uri = LauncherSettings.Favorites.getContentUri(id, 1715 false); 1716 contentResolver.delete(uri, null, null); 1717 Log.e(TAG, "Invalid package removed: " + cn); 1718 } else { 1719 // If apps can be on external storage, then we just 1720 // leave them for the user to remove (maybe add 1721 // visual treatment to it) 1722 Log.e(TAG, "Invalid package found: " + cn); 1723 } 1724 continue; 1725 } 1726 } catch (URISyntaxException e) { 1727 continue; 1728 } 1729 1730 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 1731 info = getShortcutInfo(manager, intent, context, c, iconIndex, 1732 titleIndex, mLabelCache); 1733 } else { 1734 info = getShortcutInfo(c, context, iconTypeIndex, 1735 iconPackageIndex, iconResourceIndex, iconIndex, 1736 titleIndex); 1737 1738 // App shortcuts that used to be automatically added to Launcher 1739 // didn't always have the correct intent flags set, so do that 1740 // here 1741 if (intent.getAction() != null && 1742 intent.getCategories() != null && 1743 intent.getAction().equals(Intent.ACTION_MAIN) && 1744 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { 1745 intent.addFlags( 1746 Intent.FLAG_ACTIVITY_NEW_TASK | 1747 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1748 } 1749 } 1750 1751 if (info != null) { 1752 info.id = id; 1753 info.intent = intent; 1754 container = c.getInt(containerIndex); 1755 info.container = container; 1756 info.screenId = c.getInt(screenIndex); 1757 info.cellX = c.getInt(cellXIndex); 1758 info.cellY = c.getInt(cellYIndex); 1759 info.spanX = 1; 1760 info.spanY = 1; 1761 // Skip loading items that are out of bounds 1762 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 1763 if (checkItemDimensions(info)) { 1764 Log.d(TAG, "Skipped loading out of bounds shortcut: " 1765 + info.intent); 1766 continue; 1767 } 1768 } 1769 // check & update map of what's occupied 1770 if (!checkItemPlacement(occupied, info)) { 1771 break; 1772 } 1773 1774 switch (container) { 1775 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 1776 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: 1777 sBgWorkspaceItems.add(info); 1778 break; 1779 default: 1780 // Item is in a user folder 1781 FolderInfo folderInfo = 1782 findOrMakeFolder(sBgFolders, container); 1783 folderInfo.add(info); 1784 break; 1785 } 1786 sBgItemsIdMap.put(info.id, info); 1787 1788 // now that we've loaded everthing re-save it with the 1789 // icon in case it disappears somehow. 1790 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex); 1791 } else { 1792 throw new RuntimeException("Unexpected null ShortcutInfo"); 1793 } 1794 break; 1795 1796 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 1797 id = c.getLong(idIndex); 1798 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id); 1799 1800 folderInfo.title = c.getString(titleIndex); 1801 folderInfo.id = id; 1802 container = c.getInt(containerIndex); 1803 folderInfo.container = container; 1804 folderInfo.screenId = c.getInt(screenIndex); 1805 folderInfo.cellX = c.getInt(cellXIndex); 1806 folderInfo.cellY = c.getInt(cellYIndex); 1807 folderInfo.spanX = 1; 1808 folderInfo.spanY = 1; 1809 1810 // Skip loading items that are out of bounds 1811 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 1812 if (checkItemDimensions(folderInfo)) { 1813 Log.d(TAG, "Skipped loading out of bounds folder"); 1814 continue; 1815 } 1816 } 1817 // check & update map of what's occupied 1818 if (!checkItemPlacement(occupied, folderInfo)) { 1819 break; 1820 } 1821 1822 switch (container) { 1823 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 1824 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: 1825 sBgWorkspaceItems.add(folderInfo); 1826 break; 1827 } 1828 1829 sBgItemsIdMap.put(folderInfo.id, folderInfo); 1830 sBgFolders.put(folderInfo.id, folderInfo); 1831 break; 1832 1833 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1834 // Read all Launcher-specific widget details 1835 int appWidgetId = c.getInt(appWidgetIdIndex); 1836 id = c.getLong(idIndex); 1837 1838 final AppWidgetProviderInfo provider = 1839 widgets.getAppWidgetInfo(appWidgetId); 1840 1841 if (!isSafeMode && (provider == null || provider.provider == null || 1842 provider.provider.getPackageName() == null)) { 1843 String log = "Deleting widget that isn't installed anymore: id=" 1844 + id + " appWidgetId=" + appWidgetId; 1845 Log.e(TAG, log); 1846 Launcher.sDumpLogs.add(log); 1847 itemsToRemove.add(id); 1848 } else { 1849 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 1850 provider.provider); 1851 appWidgetInfo.id = id; 1852 appWidgetInfo.screenId = c.getInt(screenIndex); 1853 appWidgetInfo.cellX = c.getInt(cellXIndex); 1854 appWidgetInfo.cellY = c.getInt(cellYIndex); 1855 appWidgetInfo.spanX = c.getInt(spanXIndex); 1856 appWidgetInfo.spanY = c.getInt(spanYIndex); 1857 int[] minSpan = Launcher.getMinSpanForWidget(context, provider); 1858 appWidgetInfo.minSpanX = minSpan[0]; 1859 appWidgetInfo.minSpanY = minSpan[1]; 1860 1861 container = c.getInt(containerIndex); 1862 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP && 1863 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 1864 Log.e(TAG, "Widget found where container != " + 1865 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); 1866 continue; 1867 } 1868 1869 appWidgetInfo.container = c.getInt(containerIndex); 1870 // Skip loading items that are out of bounds 1871 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 1872 if (checkItemDimensions(appWidgetInfo)) { 1873 Log.d(TAG, "Skipped loading out of bounds app widget"); 1874 continue; 1875 } 1876 } 1877 // check & update map of what's occupied 1878 if (!checkItemPlacement(occupied, appWidgetInfo)) { 1879 break; 1880 } 1881 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); 1882 sBgAppWidgets.add(appWidgetInfo); 1883 } 1884 break; 1885 } 1886 } catch (Exception e) { 1887 Log.w(TAG, "Desktop items loading interrupted:", e); 1888 } 1889 } 1890 } finally { 1891 if (c != null) { 1892 c.close(); 1893 } 1894 } 1895 1896 if (itemsToRemove.size() > 0) { 1897 ContentProviderClient client = contentResolver.acquireContentProviderClient( 1898 LauncherSettings.Favorites.CONTENT_URI); 1899 // Remove dead items 1900 for (long id : itemsToRemove) { 1901 if (DEBUG_LOADERS) { 1902 Log.d(TAG, "Removed id = " + id); 1903 } 1904 // Don't notify content observers 1905 try { 1906 client.delete(LauncherSettings.Favorites.getContentUri(id, false), 1907 null, null); 1908 } catch (RemoteException e) { 1909 Log.w(TAG, "Could not remove id = " + id); 1910 } 1911 } 1912 } 1913 1914 if (loadedOldDb) { 1915 Log.w(TAG, "10249126 - loadWorkspace - loadedOldDb"); 1916 long maxScreenId = 0; 1917 // If we're importing we use the old screen order. 1918 for (ItemInfo item: sBgItemsIdMap.values()) { 1919 long screenId = item.screenId; 1920 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 1921 !sBgWorkspaceScreens.contains(screenId)) { 1922 Log.w(TAG, "10249126 - loadWorkspace-loadedOldDb(" + screenId + ")"); 1923 sBgWorkspaceScreens.add(screenId); 1924 if (screenId > maxScreenId) { 1925 maxScreenId = screenId; 1926 } 1927 } 1928 } 1929 Collections.sort(sBgWorkspaceScreens); 1930 1931 // Dump the sBgWorkspaceScreens 1932 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens"); 1933 for (Long l : sBgWorkspaceScreens) { 1934 Log.w(TAG, "10249126\t- " + l); 1935 } 1936 1937 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId); 1938 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); 1939 1940 // Update the max item id after we load an old db 1941 long maxItemId = 0; 1942 // If we're importing we use the old screen order. 1943 for (ItemInfo item: sBgItemsIdMap.values()) { 1944 maxItemId = Math.max(maxItemId, item.id); 1945 } 1946 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId); 1947 } else { 1948 Log.w(TAG, "10249126 - loadWorkspace - !loadedOldDb"); 1949 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext); 1950 for (Integer i : orderedScreens.keySet()) { 1951 sBgWorkspaceScreens.add(orderedScreens.get(i)); 1952 } 1953 1954 // Remove any empty screens 1955 ArrayList<Long> unusedScreens = new ArrayList<Long>(); 1956 unusedScreens.addAll(sBgWorkspaceScreens); 1957 1958 for (ItemInfo item: sBgItemsIdMap.values()) { 1959 long screenId = item.screenId; 1960 1961 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 1962 unusedScreens.contains(screenId)) { 1963 unusedScreens.remove(screenId); 1964 } 1965 } 1966 1967 // If there are any empty screens remove them, and update. 1968 if (unusedScreens.size() != 0) { 1969 sBgWorkspaceScreens.removeAll(unusedScreens); 1970 1971 // Dump the sBgWorkspaceScreens 1972 Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens"); 1973 for (Long l : sBgWorkspaceScreens) { 1974 Log.w(TAG, "10249126\t- " + l); 1975 } 1976 1977 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); 1978 } 1979 } 1980 1981 if (DEBUG_LOADERS) { 1982 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); 1983 Log.d(TAG, "workspace layout: "); 1984 int nScreens = occupied.size(); 1985 for (int y = 0; y < countY; y++) { 1986 String line = ""; 1987 1988 Iterator<Long> iter = occupied.keySet().iterator(); 1989 while (iter.hasNext()) { 1990 long screenId = iter.next(); 1991 if (screenId > 0) { 1992 line += " | "; 1993 } 1994 for (int x = 0; x < countX; x++) { 1995 line += ((occupied.get(screenId)[x][y] != null) ? "#" : "."); 1996 } 1997 } 1998 Log.d(TAG, "[ " + line + " ]"); 1999 } 2000 } 2001 } 2002 return loadedOldDb; 2003 } 2004 2005 /** Filters the set of items who are directly or indirectly (via another container) on the 2006 * specified screen. */ 2007 private void filterCurrentWorkspaceItems(int currentScreen, 2008 ArrayList<ItemInfo> allWorkspaceItems, 2009 ArrayList<ItemInfo> currentScreenItems, 2010 ArrayList<ItemInfo> otherScreenItems) { 2011 // Purge any null ItemInfos 2012 Iterator<ItemInfo> iter = allWorkspaceItems.iterator(); 2013 while (iter.hasNext()) { 2014 ItemInfo i = iter.next(); 2015 if (i == null) { 2016 iter.remove(); 2017 } 2018 } 2019 2020 // If we aren't filtering on a screen, then the set of items to load is the full set of 2021 // items given. 2022 if (currentScreen < 0) { 2023 currentScreenItems.addAll(allWorkspaceItems); 2024 } 2025 2026 // Order the set of items by their containers first, this allows use to walk through the 2027 // list sequentially, build up a list of containers that are in the specified screen, 2028 // as well as all items in those containers. 2029 Set<Long> itemsOnScreen = new HashSet<Long>(); 2030 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() { 2031 @Override 2032 public int compare(ItemInfo lhs, ItemInfo rhs) { 2033 return (int) (lhs.container - rhs.container); 2034 } 2035 }); 2036 for (ItemInfo info : allWorkspaceItems) { 2037 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 2038 if (info.screenId == currentScreen) { 2039 currentScreenItems.add(info); 2040 itemsOnScreen.add(info.id); 2041 } else { 2042 otherScreenItems.add(info); 2043 } 2044 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 2045 currentScreenItems.add(info); 2046 itemsOnScreen.add(info.id); 2047 } else { 2048 if (itemsOnScreen.contains(info.container)) { 2049 currentScreenItems.add(info); 2050 itemsOnScreen.add(info.id); 2051 } else { 2052 otherScreenItems.add(info); 2053 } 2054 } 2055 } 2056 } 2057 2058 /** Filters the set of widgets which are on the specified screen. */ 2059 private void filterCurrentAppWidgets(int currentScreen, 2060 ArrayList<LauncherAppWidgetInfo> appWidgets, 2061 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets, 2062 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) { 2063 // If we aren't filtering on a screen, then the set of items to load is the full set of 2064 // widgets given. 2065 if (currentScreen < 0) { 2066 currentScreenWidgets.addAll(appWidgets); 2067 } 2068 2069 for (LauncherAppWidgetInfo widget : appWidgets) { 2070 if (widget == null) continue; 2071 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 2072 widget.screenId == currentScreen) { 2073 currentScreenWidgets.add(widget); 2074 } else { 2075 otherScreenWidgets.add(widget); 2076 } 2077 } 2078 } 2079 2080 /** Filters the set of folders which are on the specified screen. */ 2081 private void filterCurrentFolders(int currentScreen, 2082 HashMap<Long, ItemInfo> itemsIdMap, 2083 HashMap<Long, FolderInfo> folders, 2084 HashMap<Long, FolderInfo> currentScreenFolders, 2085 HashMap<Long, FolderInfo> otherScreenFolders) { 2086 // If we aren't filtering on a screen, then the set of items to load is the full set of 2087 // widgets given. 2088 if (currentScreen < 0) { 2089 currentScreenFolders.putAll(folders); 2090 } 2091 2092 for (long id : folders.keySet()) { 2093 ItemInfo info = itemsIdMap.get(id); 2094 FolderInfo folder = folders.get(id); 2095 if (info == null || folder == null) continue; 2096 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 2097 info.screenId == currentScreen) { 2098 currentScreenFolders.put(id, folder); 2099 } else { 2100 otherScreenFolders.put(id, folder); 2101 } 2102 } 2103 } 2104 2105 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to 2106 * right) */ 2107 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) { 2108 final LauncherAppState app = LauncherAppState.getInstance(); 2109 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); 2110 // XXX: review this 2111 Collections.sort(workspaceItems, new Comparator<ItemInfo>() { 2112 @Override 2113 public int compare(ItemInfo lhs, ItemInfo rhs) { 2114 int cellCountX = (int) grid.numColumns; 2115 int cellCountY = (int) grid.numRows; 2116 int screenOffset = cellCountX * cellCountY; 2117 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat 2118 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset + 2119 lhs.cellY * cellCountX + lhs.cellX); 2120 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset + 2121 rhs.cellY * cellCountX + rhs.cellX); 2122 return (int) (lr - rr); 2123 } 2124 }); 2125 } 2126 2127 private void bindWorkspaceScreens(final Callbacks oldCallbacks, 2128 final ArrayList<Long> orderedScreens) { 2129 Log.w(TAG, "10249126 - bindWorkspaceScreens()"); 2130 2131 // Dump the orderedScreens 2132 Log.w(TAG, "10249126 - orderedScreens"); 2133 for (Long l : sBgWorkspaceScreens) { 2134 Log.w(TAG, "10249126\t- " + l); 2135 } 2136 2137 final Runnable r = new Runnable() { 2138 @Override 2139 public void run() { 2140 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2141 if (callbacks != null) { 2142 callbacks.bindScreens(orderedScreens); 2143 } 2144 } 2145 }; 2146 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2147 } 2148 2149 private void bindWorkspaceItems(final Callbacks oldCallbacks, 2150 final ArrayList<ItemInfo> workspaceItems, 2151 final ArrayList<LauncherAppWidgetInfo> appWidgets, 2152 final HashMap<Long, FolderInfo> folders, 2153 ArrayList<Runnable> deferredBindRunnables) { 2154 2155 final boolean postOnMainThread = (deferredBindRunnables != null); 2156 2157 // Bind the workspace items 2158 int N = workspaceItems.size(); 2159 for (int i = 0; i < N; i += ITEMS_CHUNK) { 2160 final int start = i; 2161 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); 2162 final Runnable r = new Runnable() { 2163 @Override 2164 public void run() { 2165 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2166 if (callbacks != null) { 2167 callbacks.bindItems(workspaceItems, start, start+chunkSize, 2168 false); 2169 } 2170 } 2171 }; 2172 if (postOnMainThread) { 2173 deferredBindRunnables.add(r); 2174 } else { 2175 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2176 } 2177 } 2178 2179 // Bind the folders 2180 if (!folders.isEmpty()) { 2181 final Runnable r = new Runnable() { 2182 public void run() { 2183 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2184 if (callbacks != null) { 2185 callbacks.bindFolders(folders); 2186 } 2187 } 2188 }; 2189 if (postOnMainThread) { 2190 deferredBindRunnables.add(r); 2191 } else { 2192 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2193 } 2194 } 2195 2196 // Bind the widgets, one at a time 2197 N = appWidgets.size(); 2198 for (int i = 0; i < N; i++) { 2199 final LauncherAppWidgetInfo widget = appWidgets.get(i); 2200 final Runnable r = new Runnable() { 2201 public void run() { 2202 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2203 if (callbacks != null) { 2204 callbacks.bindAppWidget(widget); 2205 } 2206 } 2207 }; 2208 if (postOnMainThread) { 2209 deferredBindRunnables.add(r); 2210 } else { 2211 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2212 } 2213 } 2214 } 2215 2216 /** 2217 * Binds all loaded data to actual views on the main thread. 2218 */ 2219 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) { 2220 final long t = SystemClock.uptimeMillis(); 2221 Runnable r; 2222 2223 // Don't use these two variables in any of the callback runnables. 2224 // Otherwise we hold a reference to them. 2225 final Callbacks oldCallbacks = mCallbacks.get(); 2226 if (oldCallbacks == null) { 2227 // This launcher has exited and nobody bothered to tell us. Just bail. 2228 Log.w(TAG, "LoaderTask running with no launcher"); 2229 return; 2230 } 2231 2232 final boolean isLoadingSynchronously = (synchronizeBindPage > -1); 2233 final int currentScreen = isLoadingSynchronously ? synchronizeBindPage : 2234 oldCallbacks.getCurrentWorkspaceScreen(); 2235 2236 // Load all the items that are on the current page first (and in the process, unbind 2237 // all the existing workspace items before we call startBinding() below. 2238 unbindWorkspaceItemsOnMainThread(); 2239 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>(); 2240 ArrayList<LauncherAppWidgetInfo> appWidgets = 2241 new ArrayList<LauncherAppWidgetInfo>(); 2242 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>(); 2243 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>(); 2244 ArrayList<Long> orderedScreenIds = new ArrayList<Long>(); 2245 synchronized (sBgLock) { 2246 workspaceItems.addAll(sBgWorkspaceItems); 2247 appWidgets.addAll(sBgAppWidgets); 2248 folders.putAll(sBgFolders); 2249 itemsIdMap.putAll(sBgItemsIdMap); 2250 orderedScreenIds.addAll(sBgWorkspaceScreens); 2251 } 2252 2253 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>(); 2254 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>(); 2255 ArrayList<LauncherAppWidgetInfo> currentAppWidgets = 2256 new ArrayList<LauncherAppWidgetInfo>(); 2257 ArrayList<LauncherAppWidgetInfo> otherAppWidgets = 2258 new ArrayList<LauncherAppWidgetInfo>(); 2259 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>(); 2260 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>(); 2261 2262 // Separate the items that are on the current screen, and all the other remaining items 2263 filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems, 2264 otherWorkspaceItems); 2265 filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets, 2266 otherAppWidgets); 2267 filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders, 2268 otherFolders); 2269 sortWorkspaceItemsSpatially(currentWorkspaceItems); 2270 sortWorkspaceItemsSpatially(otherWorkspaceItems); 2271 2272 // Tell the workspace that we're about to start binding items 2273 r = new Runnable() { 2274 public void run() { 2275 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2276 if (callbacks != null) { 2277 callbacks.startBinding(); 2278 } 2279 } 2280 }; 2281 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2282 2283 bindWorkspaceScreens(oldCallbacks, orderedScreenIds); 2284 2285 // Load items on the current page 2286 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, 2287 currentFolders, null); 2288 if (isLoadingSynchronously) { 2289 r = new Runnable() { 2290 public void run() { 2291 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2292 if (callbacks != null) { 2293 callbacks.onPageBoundSynchronously(currentScreen); 2294 } 2295 } 2296 }; 2297 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2298 } 2299 2300 // Load all the remaining pages (if we are loading synchronously, we want to defer this 2301 // work until after the first render) 2302 mDeferredBindRunnables.clear(); 2303 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, 2304 (isLoadingSynchronously ? mDeferredBindRunnables : null)); 2305 2306 // Tell the workspace that we're done binding items 2307 r = new Runnable() { 2308 public void run() { 2309 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2310 if (callbacks != null) { 2311 callbacks.finishBindingItems(isUpgradePath); 2312 } 2313 2314 // If we're profiling, ensure this is the last thing in the queue. 2315 if (DEBUG_LOADERS) { 2316 Log.d(TAG, "bound workspace in " 2317 + (SystemClock.uptimeMillis()-t) + "ms"); 2318 } 2319 2320 mIsLoadingAndBindingWorkspace = false; 2321 } 2322 }; 2323 if (isLoadingSynchronously) { 2324 mDeferredBindRunnables.add(r); 2325 } else { 2326 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); 2327 } 2328 } 2329 2330 private void loadAndBindAllApps() { 2331 if (DEBUG_LOADERS) { 2332 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); 2333 } 2334 if (!mAllAppsLoaded) { 2335 loadAllApps(); 2336 synchronized (LoaderTask.this) { 2337 if (mStopped) { 2338 return; 2339 } 2340 mAllAppsLoaded = true; 2341 } 2342 } else { 2343 onlyBindAllApps(); 2344 } 2345 } 2346 2347 private void onlyBindAllApps() { 2348 final Callbacks oldCallbacks = mCallbacks.get(); 2349 if (oldCallbacks == null) { 2350 // This launcher has exited and nobody bothered to tell us. Just bail. 2351 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)"); 2352 return; 2353 } 2354 2355 // shallow copy 2356 @SuppressWarnings("unchecked") 2357 final ArrayList<ApplicationInfo> list 2358 = (ArrayList<ApplicationInfo>) mBgAllAppsList.data.clone(); 2359 Runnable r = new Runnable() { 2360 public void run() { 2361 final long t = SystemClock.uptimeMillis(); 2362 final Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2363 if (callbacks != null) { 2364 callbacks.bindAllApplications(list); 2365 } 2366 if (DEBUG_LOADERS) { 2367 Log.d(TAG, "bound all " + list.size() + " apps from cache in " 2368 + (SystemClock.uptimeMillis()-t) + "ms"); 2369 } 2370 } 2371 }; 2372 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid()); 2373 if (isRunningOnMainThread) { 2374 r.run(); 2375 } else { 2376 mHandler.post(r); 2377 } 2378 } 2379 2380 private void loadAllApps() { 2381 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 2382 2383 // Don't use these two variables in any of the callback runnables. 2384 // Otherwise we hold a reference to them. 2385 final Callbacks oldCallbacks = mCallbacks.get(); 2386 if (oldCallbacks == null) { 2387 // This launcher has exited and nobody bothered to tell us. Just bail. 2388 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)"); 2389 return; 2390 } 2391 2392 final PackageManager packageManager = mContext.getPackageManager(); 2393 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 2394 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2395 2396 // Clear the list of apps 2397 mBgAllAppsList.clear(); 2398 2399 // Query for the set of apps 2400 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 2401 List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); 2402 if (DEBUG_LOADERS) { 2403 Log.d(TAG, "queryIntentActivities took " 2404 + (SystemClock.uptimeMillis()-qiaTime) + "ms"); 2405 Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps"); 2406 } 2407 // Fail if we don't have any apps 2408 if (apps == null || apps.isEmpty()) { 2409 return; 2410 } 2411 // Sort the applications by name 2412 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 2413 Collections.sort(apps, 2414 new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache)); 2415 if (DEBUG_LOADERS) { 2416 Log.d(TAG, "sort took " 2417 + (SystemClock.uptimeMillis()-sortTime) + "ms"); 2418 } 2419 2420 // Create the ApplicationInfos 2421 for (int i = 0; i < apps.size(); i++) { 2422 // This builds the icon bitmaps. 2423 mBgAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), 2424 mIconCache, mLabelCache)); 2425 } 2426 2427 final Callbacks callbacks = tryGetCallbacks(oldCallbacks); 2428 final ArrayList<ApplicationInfo> added = mBgAllAppsList.added; 2429 mBgAllAppsList.added = new ArrayList<ApplicationInfo>(); 2430 2431 // Post callback on main thread 2432 mHandler.post(new Runnable() { 2433 public void run() { 2434 final long bindTime = SystemClock.uptimeMillis(); 2435 if (callbacks != null) { 2436 callbacks.bindAllApplications(added); 2437 if (DEBUG_LOADERS) { 2438 Log.d(TAG, "bound " + added.size() + " apps in " 2439 + (SystemClock.uptimeMillis() - bindTime) + "ms"); 2440 } 2441 } else { 2442 Log.i(TAG, "not binding apps: no Launcher activity"); 2443 } 2444 } 2445 }); 2446 2447 if (DEBUG_LOADERS) { 2448 Log.d(TAG, "Icons processed in " 2449 + (SystemClock.uptimeMillis() - loadTime) + "ms"); 2450 } 2451 } 2452 2453 public void dumpState() { 2454 synchronized (sBgLock) { 2455 Log.d(TAG, "mLoaderTask.mContext=" + mContext); 2456 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching); 2457 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped); 2458 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished); 2459 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size()); 2460 } 2461 } 2462 } 2463 2464 void enqueuePackageUpdated(PackageUpdatedTask task) { 2465 sWorker.post(task); 2466 } 2467 2468 private class PackageUpdatedTask implements Runnable { 2469 int mOp; 2470 String[] mPackages; 2471 2472 public static final int OP_NONE = 0; 2473 public static final int OP_ADD = 1; 2474 public static final int OP_UPDATE = 2; 2475 public static final int OP_REMOVE = 3; // uninstlled 2476 public static final int OP_UNAVAILABLE = 4; // external media unmounted 2477 2478 2479 public PackageUpdatedTask(int op, String[] packages) { 2480 mOp = op; 2481 mPackages = packages; 2482 } 2483 2484 public void run() { 2485 final Context context = mApp.getContext(); 2486 2487 final String[] packages = mPackages; 2488 final int N = packages.length; 2489 switch (mOp) { 2490 case OP_ADD: 2491 for (int i=0; i<N; i++) { 2492 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); 2493 mBgAllAppsList.addPackage(context, packages[i]); 2494 } 2495 break; 2496 case OP_UPDATE: 2497 for (int i=0; i<N; i++) { 2498 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); 2499 mBgAllAppsList.updatePackage(context, packages[i]); 2500 WidgetPreviewLoader.removeFromDb( 2501 mApp.getWidgetPreviewCacheDb(), packages[i]); 2502 } 2503 break; 2504 case OP_REMOVE: 2505 case OP_UNAVAILABLE: 2506 for (int i=0; i<N; i++) { 2507 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); 2508 mBgAllAppsList.removePackage(packages[i]); 2509 WidgetPreviewLoader.removeFromDb( 2510 mApp.getWidgetPreviewCacheDb(), packages[i]); 2511 } 2512 break; 2513 } 2514 2515 ArrayList<ApplicationInfo> added = null; 2516 ArrayList<ApplicationInfo> modified = null; 2517 final ArrayList<ApplicationInfo> removedApps = new ArrayList<ApplicationInfo>(); 2518 2519 if (mBgAllAppsList.added.size() > 0) { 2520 added = new ArrayList<ApplicationInfo>(mBgAllAppsList.added); 2521 mBgAllAppsList.added.clear(); 2522 } 2523 if (mBgAllAppsList.modified.size() > 0) { 2524 modified = new ArrayList<ApplicationInfo>(mBgAllAppsList.modified); 2525 mBgAllAppsList.modified.clear(); 2526 } 2527 if (mBgAllAppsList.removed.size() > 0) { 2528 removedApps.addAll(mBgAllAppsList.removed); 2529 mBgAllAppsList.removed.clear(); 2530 } 2531 2532 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; 2533 if (callbacks == null) { 2534 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading."); 2535 return; 2536 } 2537 2538 if (added != null) { 2539 // Ensure that we add all the workspace applications to the db 2540 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added); 2541 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 2542 addAndBindAddedApps(context, addedInfos, cb); 2543 } 2544 if (modified != null) { 2545 final ArrayList<ApplicationInfo> modifiedFinal = modified; 2546 2547 // Update the launcher db to reflect the changes 2548 for (ApplicationInfo a : modifiedFinal) { 2549 ArrayList<ItemInfo> infos = 2550 getItemInfoForComponentName(a.componentName); 2551 for (ItemInfo i : infos) { 2552 if (isShortcutInfoUpdateable(i)) { 2553 ShortcutInfo info = (ShortcutInfo) i; 2554 info.title = a.title.toString(); 2555 updateItemInDatabase(context, info); 2556 } 2557 } 2558 } 2559 2560 mHandler.post(new Runnable() { 2561 public void run() { 2562 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 2563 if (callbacks == cb && cb != null) { 2564 callbacks.bindAppsUpdated(modifiedFinal); 2565 } 2566 } 2567 }); 2568 } 2569 // If a package has been removed, or an app has been removed as a result of 2570 // an update (for example), make the removed callback. 2571 if (mOp == OP_REMOVE || !removedApps.isEmpty()) { 2572 final boolean packageRemoved = (mOp == OP_REMOVE); 2573 final ArrayList<String> removedPackageNames = 2574 new ArrayList<String>(Arrays.asList(packages)); 2575 2576 // Update the launcher db to reflect the removal of apps 2577 if (packageRemoved) { 2578 for (String pn : removedPackageNames) { 2579 ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn); 2580 for (ItemInfo i : infos) { 2581 deleteItemFromDatabase(context, i); 2582 } 2583 } 2584 } else { 2585 for (ApplicationInfo a : removedApps) { 2586 ArrayList<ItemInfo> infos = 2587 getItemInfoForComponentName(a.componentName); 2588 for (ItemInfo i : infos) { 2589 deleteItemFromDatabase(context, i); 2590 } 2591 } 2592 } 2593 2594 mHandler.post(new Runnable() { 2595 public void run() { 2596 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 2597 if (callbacks == cb && cb != null) { 2598 callbacks.bindComponentsRemoved(removedPackageNames, 2599 removedApps, packageRemoved); 2600 } 2601 } 2602 }); 2603 } 2604 2605 final ArrayList<Object> widgetsAndShortcuts = 2606 getSortedWidgetsAndShortcuts(context); 2607 mHandler.post(new Runnable() { 2608 @Override 2609 public void run() { 2610 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; 2611 if (callbacks == cb && cb != null) { 2612 callbacks.bindPackagesUpdated(widgetsAndShortcuts); 2613 } 2614 } 2615 }); 2616 } 2617 } 2618 2619 // Returns a list of ResolveInfos/AppWindowInfos in sorted order 2620 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) { 2621 PackageManager packageManager = context.getPackageManager(); 2622 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>(); 2623 widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders()); 2624 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); 2625 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); 2626 Collections.sort(widgetsAndShortcuts, 2627 new LauncherModel.WidgetAndShortcutNameComparator(packageManager)); 2628 return widgetsAndShortcuts; 2629 } 2630 2631 private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) { 2632 if (cn == null) { 2633 return false; 2634 } 2635 2636 try { 2637 return (pm.getActivityInfo(cn, 0) != null); 2638 } catch (NameNotFoundException e) { 2639 return false; 2640 } 2641 } 2642 2643 /** 2644 * This is called from the code that adds shortcuts from the intent receiver. This 2645 * doesn't have a Cursor, but 2646 */ 2647 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) { 2648 return getShortcutInfo(manager, intent, context, null, -1, -1, null); 2649 } 2650 2651 /** 2652 * Make an ShortcutInfo object for a shortcut that is an application. 2653 * 2654 * If c is not null, then it will be used to fill in missing data like the title and icon. 2655 */ 2656 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context, 2657 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) { 2658 ComponentName componentName = intent.getComponent(); 2659 final ShortcutInfo info = new ShortcutInfo(); 2660 if (!isValidPackageComponent(manager, componentName)) { 2661 Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName); 2662 return null; 2663 } else { 2664 try { 2665 PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0); 2666 info.initFlagsAndFirstInstallTime(pi); 2667 } catch (NameNotFoundException e) { 2668 Log.d(TAG, "getPackInfo failed for package " + 2669 componentName.getPackageName()); 2670 } 2671 } 2672 2673 // TODO: See if the PackageManager knows about this case. If it doesn't 2674 // then return null & delete this. 2675 2676 // the resource -- This may implicitly give us back the fallback icon, 2677 // but don't worry about that. All we're doing with usingFallbackIcon is 2678 // to avoid saving lots of copies of that in the database, and most apps 2679 // have icons anyway. 2680 2681 // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and 2682 // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info 2683 // via resolveActivity(). 2684 Bitmap icon = null; 2685 ResolveInfo resolveInfo = null; 2686 ComponentName oldComponent = intent.getComponent(); 2687 Intent newIntent = new Intent(intent.getAction(), null); 2688 newIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2689 newIntent.setPackage(oldComponent.getPackageName()); 2690 List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0); 2691 for (ResolveInfo i : infos) { 2692 ComponentName cn = new ComponentName(i.activityInfo.packageName, 2693 i.activityInfo.name); 2694 if (cn.equals(oldComponent)) { 2695 resolveInfo = i; 2696 } 2697 } 2698 if (resolveInfo == null) { 2699 resolveInfo = manager.resolveActivity(intent, 0); 2700 } 2701 if (resolveInfo != null) { 2702 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache); 2703 } 2704 // the db 2705 if (icon == null) { 2706 if (c != null) { 2707 icon = getIconFromCursor(c, iconIndex, context); 2708 } 2709 } 2710 // the fallback icon 2711 if (icon == null) { 2712 icon = getFallbackIcon(); 2713 info.usingFallbackIcon = true; 2714 } 2715 info.setIcon(icon); 2716 2717 // from the resource 2718 if (resolveInfo != null) { 2719 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo); 2720 if (labelCache != null && labelCache.containsKey(key)) { 2721 info.title = labelCache.get(key); 2722 } else { 2723 info.title = resolveInfo.activityInfo.loadLabel(manager); 2724 if (labelCache != null) { 2725 labelCache.put(key, info.title); 2726 } 2727 } 2728 } 2729 // from the db 2730 if (info.title == null) { 2731 if (c != null) { 2732 info.title = c.getString(titleIndex); 2733 } 2734 } 2735 // fall back to the class name of the activity 2736 if (info.title == null) { 2737 info.title = componentName.getClassName(); 2738 } 2739 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 2740 return info; 2741 } 2742 2743 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos, 2744 ItemInfoFilter f) { 2745 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>(); 2746 for (ItemInfo i : infos) { 2747 if (i instanceof ShortcutInfo) { 2748 ShortcutInfo info = (ShortcutInfo) i; 2749 ComponentName cn = info.intent.getComponent(); 2750 if (cn != null && f.filterItem(null, info, cn)) { 2751 filtered.add(info); 2752 } 2753 } else if (i instanceof FolderInfo) { 2754 FolderInfo info = (FolderInfo) i; 2755 for (ShortcutInfo s : info.contents) { 2756 ComponentName cn = s.intent.getComponent(); 2757 if (cn != null && f.filterItem(info, s, cn)) { 2758 filtered.add(s); 2759 } 2760 } 2761 } else if (i instanceof LauncherAppWidgetInfo) { 2762 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i; 2763 ComponentName cn = info.providerName; 2764 if (cn != null && f.filterItem(null, info, cn)) { 2765 filtered.add(info); 2766 } 2767 } 2768 } 2769 return new ArrayList<ItemInfo>(filtered); 2770 } 2771 2772 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) { 2773 HashSet<ItemInfo> infos = new HashSet<ItemInfo>(); 2774 ItemInfoFilter filter = new ItemInfoFilter() { 2775 @Override 2776 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { 2777 return cn.getPackageName().equals(pn); 2778 } 2779 }; 2780 return filterItemInfos(sBgItemsIdMap.values(), filter); 2781 } 2782 2783 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) { 2784 HashSet<ItemInfo> infos = new HashSet<ItemInfo>(); 2785 ItemInfoFilter filter = new ItemInfoFilter() { 2786 @Override 2787 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { 2788 return cn.equals(cname); 2789 } 2790 }; 2791 return filterItemInfos(sBgItemsIdMap.values(), filter); 2792 } 2793 2794 public static boolean isShortcutInfoUpdateable(ItemInfo i) { 2795 if (i instanceof ShortcutInfo) { 2796 ShortcutInfo info = (ShortcutInfo) i; 2797 // We need to check for ACTION_MAIN otherwise getComponent() might 2798 // return null for some shortcuts (for instance, for shortcuts to 2799 // web pages.) 2800 Intent intent = info.intent; 2801 ComponentName name = intent.getComponent(); 2802 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && 2803 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { 2804 return true; 2805 } 2806 } 2807 return false; 2808 } 2809 2810 /** 2811 * Make an ShortcutInfo object for a shortcut that isn't an application. 2812 */ 2813 private ShortcutInfo getShortcutInfo(Cursor c, Context context, 2814 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex, 2815 int titleIndex) { 2816 2817 Bitmap icon = null; 2818 final ShortcutInfo info = new ShortcutInfo(); 2819 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 2820 2821 // TODO: If there's an explicit component and we can't install that, delete it. 2822 2823 info.title = c.getString(titleIndex); 2824 2825 int iconType = c.getInt(iconTypeIndex); 2826 switch (iconType) { 2827 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE: 2828 String packageName = c.getString(iconPackageIndex); 2829 String resourceName = c.getString(iconResourceIndex); 2830 PackageManager packageManager = context.getPackageManager(); 2831 info.customIcon = false; 2832 // the resource 2833 try { 2834 Resources resources = packageManager.getResourcesForApplication(packageName); 2835 if (resources != null) { 2836 final int id = resources.getIdentifier(resourceName, null, null); 2837 icon = Utilities.createIconBitmap( 2838 mIconCache.getFullResIcon(resources, id), context); 2839 } 2840 } catch (Exception e) { 2841 // drop this. we have other places to look for icons 2842 } 2843 // the db 2844 if (icon == null) { 2845 icon = getIconFromCursor(c, iconIndex, context); 2846 } 2847 // the fallback icon 2848 if (icon == null) { 2849 icon = getFallbackIcon(); 2850 info.usingFallbackIcon = true; 2851 } 2852 break; 2853 case LauncherSettings.Favorites.ICON_TYPE_BITMAP: 2854 icon = getIconFromCursor(c, iconIndex, context); 2855 if (icon == null) { 2856 icon = getFallbackIcon(); 2857 info.customIcon = false; 2858 info.usingFallbackIcon = true; 2859 } else { 2860 info.customIcon = true; 2861 } 2862 break; 2863 default: 2864 icon = getFallbackIcon(); 2865 info.usingFallbackIcon = true; 2866 info.customIcon = false; 2867 break; 2868 } 2869 info.setIcon(icon); 2870 return info; 2871 } 2872 2873 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) { 2874 @SuppressWarnings("all") // suppress dead code warning 2875 final boolean debug = false; 2876 if (debug) { 2877 Log.d(TAG, "getIconFromCursor app=" 2878 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE))); 2879 } 2880 byte[] data = c.getBlob(iconIndex); 2881 try { 2882 return Utilities.createIconBitmap( 2883 BitmapFactory.decodeByteArray(data, 0, data.length), context); 2884 } catch (Exception e) { 2885 return null; 2886 } 2887 } 2888 2889 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen, 2890 int cellX, int cellY, boolean notify) { 2891 final ShortcutInfo info = infoFromShortcutIntent(context, data, null); 2892 if (info == null) { 2893 return null; 2894 } 2895 addItemToDatabase(context, info, container, screen, cellX, cellY, notify); 2896 2897 return info; 2898 } 2899 2900 /** 2901 * Attempts to find an AppWidgetProviderInfo that matches the given component. 2902 */ 2903 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context, 2904 ComponentName component) { 2905 List<AppWidgetProviderInfo> widgets = 2906 AppWidgetManager.getInstance(context).getInstalledProviders(); 2907 for (AppWidgetProviderInfo info : widgets) { 2908 if (info.provider.equals(component)) { 2909 return info; 2910 } 2911 } 2912 return null; 2913 } 2914 2915 /** 2916 * Returns a list of all the widgets that can handle configuration with a particular mimeType. 2917 */ 2918 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) { 2919 final PackageManager packageManager = context.getPackageManager(); 2920 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities = 2921 new ArrayList<WidgetMimeTypeHandlerData>(); 2922 2923 final Intent supportsIntent = 2924 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE); 2925 supportsIntent.setType(mimeType); 2926 2927 // Create a set of widget configuration components that we can test against 2928 final List<AppWidgetProviderInfo> widgets = 2929 AppWidgetManager.getInstance(context).getInstalledProviders(); 2930 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget = 2931 new HashMap<ComponentName, AppWidgetProviderInfo>(); 2932 for (AppWidgetProviderInfo info : widgets) { 2933 configurationComponentToWidget.put(info.configure, info); 2934 } 2935 2936 // Run through each of the intents that can handle this type of clip data, and cross 2937 // reference them with the components that are actual configuration components 2938 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent, 2939 PackageManager.MATCH_DEFAULT_ONLY); 2940 for (ResolveInfo info : activities) { 2941 final ActivityInfo activityInfo = info.activityInfo; 2942 final ComponentName infoComponent = new ComponentName(activityInfo.packageName, 2943 activityInfo.name); 2944 if (configurationComponentToWidget.containsKey(infoComponent)) { 2945 supportedConfigurationActivities.add( 2946 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info, 2947 configurationComponentToWidget.get(infoComponent))); 2948 } 2949 } 2950 return supportedConfigurationActivities; 2951 } 2952 2953 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) { 2954 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); 2955 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 2956 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); 2957 2958 if (intent == null) { 2959 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null 2960 Log.e(TAG, "Can't construct ShorcutInfo with null intent"); 2961 return null; 2962 } 2963 2964 Bitmap icon = null; 2965 boolean customIcon = false; 2966 ShortcutIconResource iconResource = null; 2967 2968 if (bitmap != null && bitmap instanceof Bitmap) { 2969 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context); 2970 customIcon = true; 2971 } else { 2972 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); 2973 if (extra != null && extra instanceof ShortcutIconResource) { 2974 try { 2975 iconResource = (ShortcutIconResource) extra; 2976 final PackageManager packageManager = context.getPackageManager(); 2977 Resources resources = packageManager.getResourcesForApplication( 2978 iconResource.packageName); 2979 final int id = resources.getIdentifier(iconResource.resourceName, null, null); 2980 icon = Utilities.createIconBitmap( 2981 mIconCache.getFullResIcon(resources, id), context); 2982 } catch (Exception e) { 2983 Log.w(TAG, "Could not load shortcut icon: " + extra); 2984 } 2985 } 2986 } 2987 2988 final ShortcutInfo info = new ShortcutInfo(); 2989 2990 if (icon == null) { 2991 if (fallbackIcon != null) { 2992 icon = fallbackIcon; 2993 } else { 2994 icon = getFallbackIcon(); 2995 info.usingFallbackIcon = true; 2996 } 2997 } 2998 info.setIcon(icon); 2999 3000 info.title = name; 3001 info.intent = intent; 3002 info.customIcon = customIcon; 3003 info.iconResource = iconResource; 3004 3005 return info; 3006 } 3007 3008 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c, 3009 int iconIndex) { 3010 // If apps can't be on SD, don't even bother. 3011 if (!mAppsCanBeOnRemoveableStorage) { 3012 return false; 3013 } 3014 // If this icon doesn't have a custom icon, check to see 3015 // what's stored in the DB, and if it doesn't match what 3016 // we're going to show, store what we are going to show back 3017 // into the DB. We do this so when we're loading, if the 3018 // package manager can't find an icon (for example because 3019 // the app is on SD) then we can use that instead. 3020 if (!info.customIcon && !info.usingFallbackIcon) { 3021 cache.put(info, c.getBlob(iconIndex)); 3022 return true; 3023 } 3024 return false; 3025 } 3026 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) { 3027 boolean needSave = false; 3028 try { 3029 if (data != null) { 3030 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length); 3031 Bitmap loaded = info.getIcon(mIconCache); 3032 needSave = !saved.sameAs(loaded); 3033 } else { 3034 needSave = true; 3035 } 3036 } catch (Exception e) { 3037 needSave = true; 3038 } 3039 if (needSave) { 3040 Log.d(TAG, "going to save icon bitmap for info=" + info); 3041 // This is slower than is ideal, but this only happens once 3042 // or when the app is updated with a new icon. 3043 updateItemInDatabase(context, info); 3044 } 3045 } 3046 3047 /** 3048 * Return an existing FolderInfo object if we have encountered this ID previously, 3049 * or make a new one. 3050 */ 3051 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) { 3052 // See if a placeholder was created for us already 3053 FolderInfo folderInfo = folders.get(id); 3054 if (folderInfo == null) { 3055 // No placeholder -- create a new instance 3056 folderInfo = new FolderInfo(); 3057 folders.put(id, folderInfo); 3058 } 3059 return folderInfo; 3060 } 3061 3062 public static final Comparator<ApplicationInfo> getAppNameComparator() { 3063 final Collator collator = Collator.getInstance(); 3064 return new Comparator<ApplicationInfo>() { 3065 public final int compare(ApplicationInfo a, ApplicationInfo b) { 3066 int result = collator.compare(a.title.toString(), b.title.toString()); 3067 if (result == 0) { 3068 result = a.componentName.compareTo(b.componentName); 3069 } 3070 return result; 3071 } 3072 }; 3073 } 3074 public static final Comparator<ApplicationInfo> APP_INSTALL_TIME_COMPARATOR 3075 = new Comparator<ApplicationInfo>() { 3076 public final int compare(ApplicationInfo a, ApplicationInfo b) { 3077 if (a.firstInstallTime < b.firstInstallTime) return 1; 3078 if (a.firstInstallTime > b.firstInstallTime) return -1; 3079 return 0; 3080 } 3081 }; 3082 public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() { 3083 final Collator collator = Collator.getInstance(); 3084 return new Comparator<AppWidgetProviderInfo>() { 3085 public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) { 3086 return collator.compare(a.label.toString(), b.label.toString()); 3087 } 3088 }; 3089 } 3090 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) { 3091 if (info.activityInfo != null) { 3092 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name); 3093 } else { 3094 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); 3095 } 3096 } 3097 public static class ShortcutNameComparator implements Comparator<ResolveInfo> { 3098 private Collator mCollator; 3099 private PackageManager mPackageManager; 3100 private HashMap<Object, CharSequence> mLabelCache; 3101 ShortcutNameComparator(PackageManager pm) { 3102 mPackageManager = pm; 3103 mLabelCache = new HashMap<Object, CharSequence>(); 3104 mCollator = Collator.getInstance(); 3105 } 3106 ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) { 3107 mPackageManager = pm; 3108 mLabelCache = labelCache; 3109 mCollator = Collator.getInstance(); 3110 } 3111 public final int compare(ResolveInfo a, ResolveInfo b) { 3112 CharSequence labelA, labelB; 3113 ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a); 3114 ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b); 3115 if (mLabelCache.containsKey(keyA)) { 3116 labelA = mLabelCache.get(keyA); 3117 } else { 3118 labelA = a.loadLabel(mPackageManager).toString(); 3119 3120 mLabelCache.put(keyA, labelA); 3121 } 3122 if (mLabelCache.containsKey(keyB)) { 3123 labelB = mLabelCache.get(keyB); 3124 } else { 3125 labelB = b.loadLabel(mPackageManager).toString(); 3126 3127 mLabelCache.put(keyB, labelB); 3128 } 3129 return mCollator.compare(labelA, labelB); 3130 } 3131 }; 3132 public static class WidgetAndShortcutNameComparator implements Comparator<Object> { 3133 private Collator mCollator; 3134 private PackageManager mPackageManager; 3135 private HashMap<Object, String> mLabelCache; 3136 WidgetAndShortcutNameComparator(PackageManager pm) { 3137 mPackageManager = pm; 3138 mLabelCache = new HashMap<Object, String>(); 3139 mCollator = Collator.getInstance(); 3140 } 3141 public final int compare(Object a, Object b) { 3142 String labelA, labelB; 3143 if (mLabelCache.containsKey(a)) { 3144 labelA = mLabelCache.get(a); 3145 } else { 3146 labelA = (a instanceof AppWidgetProviderInfo) ? 3147 ((AppWidgetProviderInfo) a).label : 3148 ((ResolveInfo) a).loadLabel(mPackageManager).toString(); 3149 mLabelCache.put(a, labelA); 3150 } 3151 if (mLabelCache.containsKey(b)) { 3152 labelB = mLabelCache.get(b); 3153 } else { 3154 labelB = (b instanceof AppWidgetProviderInfo) ? 3155 ((AppWidgetProviderInfo) b).label : 3156 ((ResolveInfo) b).loadLabel(mPackageManager).toString(); 3157 mLabelCache.put(b, labelB); 3158 } 3159 return mCollator.compare(labelA, labelB); 3160 } 3161 }; 3162 3163 public void dumpState() { 3164 Log.d(TAG, "mCallbacks=" + mCallbacks); 3165 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data); 3166 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added); 3167 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed); 3168 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified); 3169 if (mLoaderTask != null) { 3170 mLoaderTask.dumpState(); 3171 } else { 3172 Log.d(TAG, "mLoaderTask=null"); 3173 } 3174 } 3175} 3176