blob: 7a56bcd7b90d7647f04bfa8ea9f1af430cf83efe [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Romain Guy629de3e2010-01-13 12:20:59 -080019import android.appwidget.AppWidgetManager;
20import android.appwidget.AppWidgetProviderInfo;
Joe Onoratof99f8c12009-10-31 17:27:36 -040021import android.content.BroadcastReceiver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080022import android.content.ComponentName;
Romain Guy5c16f3e2010-01-12 17:24:58 -080023import android.content.ContentProviderClient;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080027import android.content.Intent.ShortcutIconResource;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.Context;
29import android.content.pm.ActivityInfo;
30import android.content.pm.PackageManager;
Romain Guy5c16f3e2010-01-12 17:24:58 -080031import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.content.pm.ResolveInfo;
33import android.content.res.Resources;
34import android.database.Cursor;
35import android.graphics.Bitmap;
36import android.graphics.BitmapFactory;
37import android.net.Uri;
Joe Onorato0589f0f2010-02-08 13:44:00 -080038import android.os.Parcelable;
Romain Guy5c16f3e2010-01-12 17:24:58 -080039import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040040import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041import android.os.Process;
Joe Onorato9c1289c2009-08-17 11:03:03 -040042import android.os.SystemClock;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043
Joe Onorato9c1289c2009-08-17 11:03:03 -040044import java.lang.ref.WeakReference;
45import java.net.URISyntaxException;
46import java.text.Collator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import java.util.ArrayList;
Joe Onorato56d82912010-03-07 14:32:10 -050048import java.util.Arrays;
Joe Onorato9c1289c2009-08-17 11:03:03 -040049import java.util.Comparator;
50import java.util.Collections;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import java.util.HashMap;
52import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053
Romain Guyedcce092010-03-04 13:03:17 -080054import com.android.launcher.R;
55
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056/**
57 * Maintains in-memory state of the Launcher. It is expected that there should be only one
58 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070059 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080060 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040061public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080062 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040063 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070064
Daniel Sandler2ff10b32010-04-16 15:06:06 -040065 private int mAllAppsLoadDelay; // milliseconds between batches
Daniel Sandlerdca66122010-04-13 16:23:58 -040066
Joe Onoratof99f8c12009-10-31 17:27:36 -040067 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040068 private final Object mLock = new Object();
69 private DeferredHandler mHandler = new DeferredHandler();
70 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080071
Joe Onoratof99f8c12009-10-31 17:27:36 -040072 private boolean mBeforeFirstLoad = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -040073 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080074
Joe Onorato0589f0f2010-02-08 13:44:00 -080075 private AllAppsList mAllAppsList;
76 private IconCache mIconCache;
77
78 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079
Joe Onorato9c1289c2009-08-17 11:03:03 -040080 public interface Callbacks {
81 public int getCurrentWorkspaceScreen();
82 public void startBinding();
83 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050084 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040085 public void finishBindingItems();
86 public void bindAppWidget(LauncherAppWidgetInfo info);
87 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -050088 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
89 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
90 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
Daniel Sandlerdca66122010-04-13 16:23:58 -040091 public int getAppBatchSize();
Joe Onorato9c1289c2009-08-17 11:03:03 -040092 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080093
Joe Onorato0589f0f2010-02-08 13:44:00 -080094 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -040095 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -080096 mAllAppsList = new AllAppsList(iconCache);
97 mIconCache = iconCache;
98
99 mDefaultIcon = Utilities.createIconBitmap(
100 app.getPackageManager().getDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400101
102 mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800103 }
104
Joe Onorato56d82912010-03-07 14:32:10 -0500105 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800106 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400107 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108
Joe Onorato9c1289c2009-08-17 11:03:03 -0400109 /**
110 * Adds an item to the DB if it was not created previously, or move it to a new
111 * <container, screen, cellX, cellY>
112 */
113 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
114 int screen, int cellX, int cellY) {
115 if (item.container == ItemInfo.NO_ID) {
116 // From all apps
117 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
118 } else {
119 // From somewhere else
120 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 }
122 }
123
124 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400125 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700126 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400127 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
128 int cellX, int cellY) {
129 item.container = container;
130 item.screen = screen;
131 item.cellX = cellX;
132 item.cellY = cellY;
133
134 final ContentValues values = new ContentValues();
135 final ContentResolver cr = context.getContentResolver();
136
137 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
138 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
139 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
140 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
141
142 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700143 }
144
145 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400146 * Returns true if the shortcuts already exists in the database.
147 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400149 static boolean shortcutExists(Context context, String title, Intent intent) {
150 final ContentResolver cr = context.getContentResolver();
151 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
152 new String[] { "title", "intent" }, "title=? and intent=?",
153 new String[] { title, intent.toUri(0) }, null);
154 boolean result = false;
155 try {
156 result = c.moveToFirst();
157 } finally {
158 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800159 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400160 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700161 }
162
Joe Onorato9c1289c2009-08-17 11:03:03 -0400163 /**
164 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
165 */
166 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
167 final ContentResolver cr = context.getContentResolver();
168 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
169 "_id=? and (itemType=? or itemType=?)",
170 new String[] { String.valueOf(id),
171 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
172 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700173
Joe Onorato9c1289c2009-08-17 11:03:03 -0400174 try {
175 if (c.moveToFirst()) {
176 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
177 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
178 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
179 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
180 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
181 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800182
Joe Onorato9c1289c2009-08-17 11:03:03 -0400183 FolderInfo folderInfo = null;
184 switch (c.getInt(itemTypeIndex)) {
185 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
186 folderInfo = findOrMakeUserFolder(folderList, id);
187 break;
188 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
189 folderInfo = findOrMakeLiveFolder(folderList, id);
190 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700191 }
192
Joe Onorato9c1289c2009-08-17 11:03:03 -0400193 folderInfo.title = c.getString(titleIndex);
194 folderInfo.id = id;
195 folderInfo.container = c.getInt(containerIndex);
196 folderInfo.screen = c.getInt(screenIndex);
197 folderInfo.cellX = c.getInt(cellXIndex);
198 folderInfo.cellY = c.getInt(cellYIndex);
199
200 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700201 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400202 } finally {
203 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700204 }
205
206 return null;
207 }
208
Joe Onorato9c1289c2009-08-17 11:03:03 -0400209 /**
210 * Add an item to the database in a specified container. Sets the container, screen, cellX and
211 * cellY fields of the item. Also assigns an ID to the item.
212 */
213 static void addItemToDatabase(Context context, ItemInfo item, long container,
214 int screen, int cellX, int cellY, boolean notify) {
215 item.container = container;
216 item.screen = screen;
217 item.cellX = cellX;
218 item.cellY = cellY;
219
220 final ContentValues values = new ContentValues();
221 final ContentResolver cr = context.getContentResolver();
222
223 item.onAddToDatabase(values);
224
225 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
226 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
227
228 if (result != null) {
229 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700230 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700231 }
232
Joe Onorato9c1289c2009-08-17 11:03:03 -0400233 /**
234 * Update an item to the database in a specified container.
235 */
236 static void updateItemInDatabase(Context context, ItemInfo item) {
237 final ContentValues values = new ContentValues();
238 final ContentResolver cr = context.getContentResolver();
239
240 item.onAddToDatabase(values);
241
242 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
243 }
244
245 /**
246 * Removes the specified item from the database
247 * @param context
248 * @param item
249 */
250 static void deleteItemFromDatabase(Context context, ItemInfo item) {
251 final ContentResolver cr = context.getContentResolver();
252
253 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
254 }
255
256 /**
257 * Remove the contents of the specified folder from the database
258 */
259 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
260 final ContentResolver cr = context.getContentResolver();
261
262 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
263 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
264 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
265 }
266
267 /**
268 * Set this as the current Launcher activity object for the loader.
269 */
270 public void initialize(Callbacks callbacks) {
271 synchronized (mLock) {
272 mCallbacks = new WeakReference<Callbacks>(callbacks);
273 }
274 }
275
276 public void startLoader(Context context, boolean isLaunching) {
277 mLoader.startLoader(context, isLaunching);
278 }
279
280 public void stopLoader() {
281 mLoader.stopLoader();
282 }
283
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700284 /**
285 * We pick up most of the changes to all apps.
286 */
287 public void setAllAppsDirty() {
288 mLoader.setAllAppsDirty();
289 }
290
Joe Onorato9c1289c2009-08-17 11:03:03 -0400291 public void setWorkspaceDirty() {
292 mLoader.setWorkspaceDirty();
293 }
294
295 /**
296 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
297 * ACTION_PACKAGE_CHANGED.
298 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400299 public void onReceive(Context context, Intent intent) {
300 // Use the app as the context.
301 context = mApp;
302
Joe Onorato9c1289c2009-08-17 11:03:03 -0400303 ArrayList<ApplicationInfo> added = null;
304 ArrayList<ApplicationInfo> removed = null;
305 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400306
307 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400308 if (mBeforeFirstLoad) {
309 // If we haven't even loaded yet, don't bother, since we'll just pick
310 // up the changes.
311 return;
312 }
313
Joe Onorato9c1289c2009-08-17 11:03:03 -0400314 final String action = intent.getAction();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400315
Joe Onorato64e6be72010-03-05 15:05:52 -0500316 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
317 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
318 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
319 final String packageName = intent.getData().getSchemeSpecificPart();
320 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400321
Joe Onorato64e6be72010-03-05 15:05:52 -0500322 if (packageName == null || packageName.length() == 0) {
323 // they sent us a bad intent
324 return;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400325 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500326
327 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400328 mAllAppsList.updatePackage(context, packageName);
Joe Onorato64e6be72010-03-05 15:05:52 -0500329 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
330 if (!replacing) {
331 mAllAppsList.removePackage(packageName);
332 }
333 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
334 // later, we will update the package at this time
335 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
336 if (!replacing) {
337 mAllAppsList.addPackage(context, packageName);
338 } else {
339 mAllAppsList.updatePackage(context, packageName);
340 }
341 }
Joe Onorato56d82912010-03-07 14:32:10 -0500342
343 if (mAllAppsList.added.size() > 0) {
344 added = mAllAppsList.added;
345 mAllAppsList.added = new ArrayList<ApplicationInfo>();
346 }
347 if (mAllAppsList.removed.size() > 0) {
348 removed = mAllAppsList.removed;
349 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
350 for (ApplicationInfo info: removed) {
351 mIconCache.remove(info.intent.getComponent());
352 }
353 }
354 if (mAllAppsList.modified.size() > 0) {
355 modified = mAllAppsList.modified;
356 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
357 }
358
359 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
360 if (callbacks == null) {
361 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
362 return;
363 }
364
365 if (added != null) {
366 final ArrayList<ApplicationInfo> addedFinal = added;
367 mHandler.post(new Runnable() {
368 public void run() {
369 callbacks.bindAppsAdded(addedFinal);
370 }
371 });
372 }
373 if (modified != null) {
374 final ArrayList<ApplicationInfo> modifiedFinal = modified;
375 mHandler.post(new Runnable() {
376 public void run() {
377 callbacks.bindAppsUpdated(modifiedFinal);
378 }
379 });
380 }
381 if (removed != null) {
382 final ArrayList<ApplicationInfo> removedFinal = removed;
383 mHandler.post(new Runnable() {
384 public void run() {
385 callbacks.bindAppsRemoved(removedFinal);
386 }
387 });
388 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500389 } else {
390 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
391 String packages[] = intent.getStringArrayExtra(
392 Intent.EXTRA_CHANGED_PACKAGE_LIST);
393 if (packages == null || packages.length == 0) {
394 return;
395 }
Joe Onorato56d82912010-03-07 14:32:10 -0500396 setAllAppsDirty();
397 setWorkspaceDirty();
398 startLoader(context, false);
Joe Onorato64e6be72010-03-05 15:05:52 -0500399 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
400 String packages[] = intent.getStringArrayExtra(
401 Intent.EXTRA_CHANGED_PACKAGE_LIST);
402 if (packages == null || packages.length == 0) {
403 return;
404 }
Joe Onorato56d82912010-03-07 14:32:10 -0500405 setAllAppsDirty();
406 setWorkspaceDirty();
407 startLoader(context, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400408 }
409 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400410 }
411 }
412
413 public class Loader {
414 private static final int ITEMS_CHUNK = 6;
415
416 private LoaderThread mLoaderThread;
417
418 private int mLastWorkspaceSeq = 0;
419 private int mWorkspaceSeq = 1;
420
421 private int mLastAllAppsSeq = 0;
422 private int mAllAppsSeq = 1;
423
Romain Guy84f296c2009-11-04 15:00:44 -0800424 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
425 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500426 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400427
428 /**
429 * Call this from the ui thread so the handler is initialized on the correct thread.
430 */
431 public Loader() {
432 }
433
434 public void startLoader(Context context, boolean isLaunching) {
435 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800436 if (DEBUG_LOADERS) {
437 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
438 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400439 // Don't bother to start the thread if we know it's not going to do anything
Joe Onoratoac033302010-04-13 17:19:18 -0700440 if (mCallbacks != null && mCallbacks.get() != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400441 LoaderThread oldThread = mLoaderThread;
442 if (oldThread != null) {
443 if (oldThread.isLaunching()) {
444 // don't downgrade isLaunching if we're already running
445 isLaunching = true;
446 }
447 oldThread.stopLocked();
448 }
449 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
450 mLoaderThread.start();
451 }
452 }
453 }
454
455 public void stopLoader() {
456 synchronized (mLock) {
457 if (mLoaderThread != null) {
458 mLoaderThread.stopLocked();
459 }
460 }
461 }
462
463 public void setWorkspaceDirty() {
464 synchronized (mLock) {
465 mWorkspaceSeq++;
466 }
467 }
468
469 public void setAllAppsDirty() {
470 synchronized (mLock) {
471 mAllAppsSeq++;
472 }
473 }
474
475 /**
476 * Runnable for the thread that loads the contents of the launcher:
477 * - workspace icons
478 * - widgets
479 * - all apps icons
480 */
481 private class LoaderThread extends Thread {
482 private Context mContext;
483 private Thread mWaitThread;
484 private boolean mIsLaunching;
485 private boolean mStopped;
486 private boolean mWorkspaceDoneBinding;
487
488 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
489 mContext = context;
490 mWaitThread = waitThread;
491 mIsLaunching = isLaunching;
492 }
493
494 boolean isLaunching() {
495 return mIsLaunching;
496 }
497
498 /**
499 * If another LoaderThread was supplied, we need to wait for that to finish before
500 * we start our processing. This keeps the ordering of the setting and clearing
501 * of the dirty flags correct by making sure we don't start processing stuff until
502 * they've had a chance to re-set them. We do this waiting the worker thread, not
503 * the ui thread to avoid ANRs.
504 */
505 private void waitForOtherThread() {
506 if (mWaitThread != null) {
507 boolean done = false;
508 while (!done) {
509 try {
510 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700511 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400512 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800513 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400514 }
515 }
516 mWaitThread = null;
517 }
518 }
519
520 public void run() {
521 waitForOtherThread();
522
523 // Elevate priority when Home launches for the first time to avoid
524 // starving at boot time. Staring at a blank home is not cool.
525 synchronized (mLock) {
526 android.os.Process.setThreadPriority(mIsLaunching
527 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
528 }
529
530 // Load the workspace only if it's dirty.
531 int workspaceSeq;
532 boolean workspaceDirty;
533 synchronized (mLock) {
534 workspaceSeq = mWorkspaceSeq;
535 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
536 }
537 if (workspaceDirty) {
538 loadWorkspace();
539 }
540 synchronized (mLock) {
541 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
542 if (mStopped) {
543 return;
544 }
545 if (workspaceSeq == mWorkspaceSeq) {
546 mLastWorkspaceSeq = mWorkspaceSeq;
547 }
548 }
549
550 // Bind the workspace
551 bindWorkspace();
552
553 // Wait until the either we're stopped or the other threads are done.
554 // This way we don't start loading all apps until the workspace has settled
555 // down.
556 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500557 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400558 public void run() {
559 synchronized (LoaderThread.this) {
560 mWorkspaceDoneBinding = true;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800561 if (DEBUG_LOADERS) {
562 Log.d(TAG, "done with workspace");
563 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400564 LoaderThread.this.notify();
565 }
566 }
567 });
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800568 if (DEBUG_LOADERS) {
569 Log.d(TAG, "waiting to be done with workspace");
570 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400571 while (!mStopped && !mWorkspaceDoneBinding) {
572 try {
573 this.wait();
574 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800575 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400576 }
577 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800578 if (DEBUG_LOADERS) {
579 Log.d(TAG, "done waiting to be done with workspace");
580 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400581 }
582
Daniel Sandlerdca66122010-04-13 16:23:58 -0400583 // Whew! Hard work done.
584 synchronized (mLock) {
585 if (mIsLaunching) {
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400586 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Daniel Sandlerdca66122010-04-13 16:23:58 -0400587 }
588 }
589
Joe Onorato9c1289c2009-08-17 11:03:03 -0400590 // Load all apps if they're dirty
591 int allAppsSeq;
592 boolean allAppsDirty;
593 synchronized (mLock) {
594 allAppsSeq = mAllAppsSeq;
595 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800596 if (DEBUG_LOADERS) {
597 Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
598 + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
599 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400600 }
601 if (allAppsDirty) {
Daniel Sandlerdca66122010-04-13 16:23:58 -0400602 loadAndBindAllApps();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400603 }
604 synchronized (mLock) {
605 // If we're not stopped, and nobody has incremented mAllAppsSeq.
606 if (mStopped) {
607 return;
608 }
609 if (allAppsSeq == mAllAppsSeq) {
610 mLastAllAppsSeq = mAllAppsSeq;
611 }
612 }
613
Joe Onorato9c1289c2009-08-17 11:03:03 -0400614 // Clear out this reference, otherwise we end up holding it until all of the
615 // callback runnables are done.
616 mContext = null;
617
618 synchronized (mLock) {
619 // Setting the reference is atomic, but we can't do it inside the other critical
620 // sections.
621 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400622 }
623 }
624
625 public void stopLocked() {
626 synchronized (LoaderThread.this) {
627 mStopped = true;
628 this.notify();
629 }
630 }
631
632 /**
633 * Gets the callbacks object. If we've been stopped, or if the launcher object
Joe Onoratoc131b742010-03-11 15:45:05 -0800634 * has somehow been garbage collected, return null instead. Pass in the Callbacks
635 * object that was around when the deferred message was scheduled, and if there's
636 * a new Callbacks object around then also return null. This will save us from
637 * calling onto it with data that will be ignored.
Joe Onorato9c1289c2009-08-17 11:03:03 -0400638 */
Joe Onoratoc131b742010-03-11 15:45:05 -0800639 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400640 synchronized (mLock) {
641 if (mStopped) {
642 return null;
643 }
644
Joe Onoratoac033302010-04-13 17:19:18 -0700645 if (mCallbacks == null) {
646 return null;
647 }
648
Joe Onorato9c1289c2009-08-17 11:03:03 -0400649 final Callbacks callbacks = mCallbacks.get();
Joe Onoratoc131b742010-03-11 15:45:05 -0800650 if (callbacks != oldCallbacks) {
651 return null;
652 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400653 if (callbacks == null) {
654 Log.w(TAG, "no mCallbacks");
655 return null;
656 }
657
658 return callbacks;
659 }
660 }
661
662 private void loadWorkspace() {
663 long t = SystemClock.uptimeMillis();
664
665 final Context context = mContext;
666 final ContentResolver contentResolver = context.getContentResolver();
667 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800668 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800669 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400670
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400671 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500672 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800673 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400674
Romain Guy5c16f3e2010-01-12 17:24:58 -0800675 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
676
Joe Onorato9c1289c2009-08-17 11:03:03 -0400677 final Cursor c = contentResolver.query(
678 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
679
680 try {
681 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
682 final int intentIndex = c.getColumnIndexOrThrow
683 (LauncherSettings.Favorites.INTENT);
684 final int titleIndex = c.getColumnIndexOrThrow
685 (LauncherSettings.Favorites.TITLE);
686 final int iconTypeIndex = c.getColumnIndexOrThrow(
687 LauncherSettings.Favorites.ICON_TYPE);
688 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
689 final int iconPackageIndex = c.getColumnIndexOrThrow(
690 LauncherSettings.Favorites.ICON_PACKAGE);
691 final int iconResourceIndex = c.getColumnIndexOrThrow(
692 LauncherSettings.Favorites.ICON_RESOURCE);
693 final int containerIndex = c.getColumnIndexOrThrow(
694 LauncherSettings.Favorites.CONTAINER);
695 final int itemTypeIndex = c.getColumnIndexOrThrow(
696 LauncherSettings.Favorites.ITEM_TYPE);
697 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
698 LauncherSettings.Favorites.APPWIDGET_ID);
699 final int screenIndex = c.getColumnIndexOrThrow(
700 LauncherSettings.Favorites.SCREEN);
701 final int cellXIndex = c.getColumnIndexOrThrow
702 (LauncherSettings.Favorites.CELLX);
703 final int cellYIndex = c.getColumnIndexOrThrow
704 (LauncherSettings.Favorites.CELLY);
705 final int spanXIndex = c.getColumnIndexOrThrow
706 (LauncherSettings.Favorites.SPANX);
707 final int spanYIndex = c.getColumnIndexOrThrow(
708 LauncherSettings.Favorites.SPANY);
709 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
710 final int displayModeIndex = c.getColumnIndexOrThrow(
711 LauncherSettings.Favorites.DISPLAY_MODE);
712
Joe Onorato0589f0f2010-02-08 13:44:00 -0800713 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400714 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400715 LauncherAppWidgetInfo appWidgetInfo;
716 int container;
717 long id;
718 Intent intent;
719
720 while (!mStopped && c.moveToNext()) {
721 try {
722 int itemType = c.getInt(itemTypeIndex);
723
724 switch (itemType) {
725 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
726 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
727 intentDescription = c.getString(intentIndex);
728 try {
729 intent = Intent.parseUri(intentDescription, 0);
730 } catch (URISyntaxException e) {
731 continue;
732 }
733
734 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500735 info = getShortcutInfo(manager, intent, context, c, iconIndex,
736 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400737 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800738 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500739 iconPackageIndex, iconResourceIndex, iconIndex,
740 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400741 }
742
743 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500744 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400745
Joe Onorato56d82912010-03-07 14:32:10 -0500746 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400747 info.id = c.getLong(idIndex);
748 container = c.getInt(containerIndex);
749 info.container = container;
750 info.screen = c.getInt(screenIndex);
751 info.cellX = c.getInt(cellXIndex);
752 info.cellY = c.getInt(cellYIndex);
753
754 switch (container) {
755 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
756 mItems.add(info);
757 break;
758 default:
759 // Item is in a user folder
760 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500761 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400762 folderInfo.add(info);
763 break;
764 }
Joe Onorato56d82912010-03-07 14:32:10 -0500765 } else {
766 // Failed to load the shortcut, probably because the
767 // activity manager couldn't resolve it (maybe the app
768 // was uninstalled), or the db row was somehow screwed up.
769 // Delete it.
770 id = c.getLong(idIndex);
771 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
772 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
773 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400774 }
775 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400776
Joe Onoratoad72e172009-11-06 16:25:04 -0500777 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400778 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500779 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400780
781 folderInfo.title = c.getString(titleIndex);
782
783 folderInfo.id = id;
784 container = c.getInt(containerIndex);
785 folderInfo.container = container;
786 folderInfo.screen = c.getInt(screenIndex);
787 folderInfo.cellX = c.getInt(cellXIndex);
788 folderInfo.cellY = c.getInt(cellYIndex);
789
790 switch (container) {
791 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
792 mItems.add(folderInfo);
793 break;
794 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500795
796 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400797 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500798
Joe Onorato9c1289c2009-08-17 11:03:03 -0400799 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400800 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800801 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400802
Romain Guy5c16f3e2010-01-12 17:24:58 -0800803 // Make sure the live folder exists
804 final ProviderInfo providerInfo =
805 context.getPackageManager().resolveContentProvider(
806 uri.getAuthority(), 0);
807
808 if (providerInfo == null && !isSafeMode) {
809 itemsToRemove.add(id);
810 } else {
811 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
812
813 intentDescription = c.getString(intentIndex);
814 intent = null;
815 if (intentDescription != null) {
816 try {
817 intent = Intent.parseUri(intentDescription, 0);
818 } catch (URISyntaxException e) {
819 // Ignore, a live folder might not have a base intent
820 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400821 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800822
823 liveFolderInfo.title = c.getString(titleIndex);
824 liveFolderInfo.id = id;
825 liveFolderInfo.uri = uri;
826 container = c.getInt(containerIndex);
827 liveFolderInfo.container = container;
828 liveFolderInfo.screen = c.getInt(screenIndex);
829 liveFolderInfo.cellX = c.getInt(cellXIndex);
830 liveFolderInfo.cellY = c.getInt(cellYIndex);
831 liveFolderInfo.baseIntent = intent;
832 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
833
834 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
835 iconResourceIndex, liveFolderInfo);
836
837 switch (container) {
838 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
839 mItems.add(liveFolderInfo);
840 break;
841 }
842 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400843 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400844 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500845
Joe Onorato9c1289c2009-08-17 11:03:03 -0400846 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
847 // Read all Launcher-specific widget details
848 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800849 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400850
Romain Guy629de3e2010-01-13 12:20:59 -0800851 final AppWidgetProviderInfo provider =
852 widgets.getAppWidgetInfo(appWidgetId);
853
854 if (!isSafeMode && (provider == null || provider.provider == null ||
855 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700856 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
857 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800858 itemsToRemove.add(id);
859 } else {
860 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
861 appWidgetInfo.id = id;
862 appWidgetInfo.screen = c.getInt(screenIndex);
863 appWidgetInfo.cellX = c.getInt(cellXIndex);
864 appWidgetInfo.cellY = c.getInt(cellYIndex);
865 appWidgetInfo.spanX = c.getInt(spanXIndex);
866 appWidgetInfo.spanY = c.getInt(spanYIndex);
867
868 container = c.getInt(containerIndex);
869 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
870 Log.e(TAG, "Widget found where container "
871 + "!= CONTAINER_DESKTOP -- ignoring!");
872 continue;
873 }
874 appWidgetInfo.container = c.getInt(containerIndex);
875
876 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400877 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400878 break;
879 }
880 } catch (Exception e) {
881 Log.w(TAG, "Desktop items loading interrupted:", e);
882 }
883 }
884 } finally {
885 c.close();
886 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800887
888 if (itemsToRemove.size() > 0) {
889 ContentProviderClient client = contentResolver.acquireContentProviderClient(
890 LauncherSettings.Favorites.CONTENT_URI);
891 // Remove dead items
892 for (long id : itemsToRemove) {
893 if (DEBUG_LOADERS) {
894 Log.d(TAG, "Removed id = " + id);
895 }
896 // Don't notify content observers
897 try {
898 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
899 null, null);
900 } catch (RemoteException e) {
901 Log.w(TAG, "Could not remove id = " + id);
902 }
903 }
904 }
905
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800906 if (DEBUG_LOADERS) {
907 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
908 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400909 }
910
911 /**
912 * Read everything out of our database.
913 */
914 private void bindWorkspace() {
915 final long t = SystemClock.uptimeMillis();
916
917 // Don't use these two variables in any of the callback runnables.
918 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800919 final Callbacks oldCallbacks = mCallbacks.get();
920 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400921 // This launcher has exited and nobody bothered to tell us. Just bail.
922 Log.w(TAG, "LoaderThread running with no launcher");
923 return;
924 }
925
926 int N;
927 // Tell the workspace that we're about to start firing items at it
928 mHandler.post(new Runnable() {
929 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800930 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400931 if (callbacks != null) {
932 callbacks.startBinding();
933 }
934 }
935 });
936 // Add the items to the workspace.
937 N = mItems.size();
938 for (int i=0; i<N; i+=ITEMS_CHUNK) {
939 final int start = i;
940 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
941 mHandler.post(new Runnable() {
942 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800943 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400944 if (callbacks != null) {
945 callbacks.bindItems(mItems, start, start+chunkSize);
946 }
947 }
948 });
949 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500950 mHandler.post(new Runnable() {
951 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800952 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -0500953 if (callbacks != null) {
954 callbacks.bindFolders(mFolders);
955 }
956 }
957 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400958 // Wait until the queue goes empty.
959 mHandler.postIdle(new Runnable() {
960 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800961 if (DEBUG_LOADERS) {
962 Log.d(TAG, "Going to start binding widgets soon.");
963 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400964 }
965 });
966 // Bind the widgets, one at a time.
967 // WARNING: this is calling into the workspace from the background thread,
968 // but since getCurrentScreen() just returns the int, we should be okay. This
969 // is just a hint for the order, and if it's wrong, we'll be okay.
970 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -0800971 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400972 N = mAppWidgets.size();
973 // once for the current screen
974 for (int i=0; i<N; i++) {
975 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
976 if (widget.screen == currentScreen) {
977 mHandler.post(new Runnable() {
978 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800979 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400980 if (callbacks != null) {
981 callbacks.bindAppWidget(widget);
982 }
983 }
984 });
985 }
986 }
987 // once for the other screens
988 for (int i=0; i<N; i++) {
989 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
990 if (widget.screen != currentScreen) {
991 mHandler.post(new Runnable() {
992 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800993 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400994 if (callbacks != null) {
995 callbacks.bindAppWidget(widget);
996 }
997 }
998 });
999 }
1000 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001001 // Tell the workspace that we're done.
1002 mHandler.post(new Runnable() {
1003 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001004 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001005 if (callbacks != null) {
1006 callbacks.finishBindingItems();
1007 }
1008 }
1009 });
1010 // If we're profiling, this is the last thing in the queue.
1011 mHandler.post(new Runnable() {
1012 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001013 if (DEBUG_LOADERS) {
1014 Log.d(TAG, "bound workspace in "
1015 + (SystemClock.uptimeMillis()-t) + "ms");
1016 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001017 if (Launcher.PROFILE_ROTATE) {
1018 android.os.Debug.stopMethodTracing();
1019 }
1020 }
1021 });
1022 }
1023
Daniel Sandlerdca66122010-04-13 16:23:58 -04001024 private void loadAndBindAllApps() {
1025 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1026
Joe Onorato9c1289c2009-08-17 11:03:03 -04001027 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1028 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1029
Joe Onoratoc131b742010-03-11 15:45:05 -08001030 final Callbacks callbacks = mCallbacks.get();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001031 if (callbacks == null) {
1032 return;
1033 }
1034
Joe Onorato0589f0f2010-02-08 13:44:00 -08001035 final PackageManager packageManager = mContext.getPackageManager();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001036 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
1037
Daniel Sandlerdca66122010-04-13 16:23:58 -04001038 int N;
1039 int batchSize = callbacks.getAppBatchSize();
1040
Joe Onorato9c1289c2009-08-17 11:03:03 -04001041 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -04001042 mBeforeFirstLoad = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001043 mAllAppsList.clear();
Daniel Sandlerdca66122010-04-13 16:23:58 -04001044 if (apps == null) return;
1045 N = apps.size();
1046 if (batchSize <= 0)
1047 batchSize = N;
1048 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001049
Daniel Sandlerdca66122010-04-13 16:23:58 -04001050 int i=0;
1051 while (i < N && !mStopped) {
1052 synchronized (mLock) {
1053 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1054
1055 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001056 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001057 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001058 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001059 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001060 if (DEBUG_LOADERS) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001061 Log.d(TAG, "batch of " + batchSize + " icons processed in "
1062 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001063 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001064 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001065
1066 mHandler.post(bindAllAppsTask);
1067
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001068 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001069 try {
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001070 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001071 } catch (InterruptedException exc) { }
1072 }
1073 }
1074
1075 if (DEBUG_LOADERS) {
1076 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001077 + (SystemClock.uptimeMillis()-t) + "ms"
1078 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001079 }
1080 }
1081
Daniel Sandlerdca66122010-04-13 16:23:58 -04001082 final Runnable bindAllAppsTask = new Runnable() {
1083 public void run() {
1084 final long t = SystemClock.uptimeMillis();
1085 int count = 0;
1086 Callbacks callbacks = null;
1087 ArrayList<ApplicationInfo> results = null;
1088 synchronized (mLock) {
1089 mHandler.cancelRunnable(this);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001090
Daniel Sandlerdca66122010-04-13 16:23:58 -04001091 results = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
1092 // We're adding this now, so clear out this so we don't re-send them.
1093 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1094 count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001095
Daniel Sandlerdca66122010-04-13 16:23:58 -04001096 callbacks = tryGetCallbacks(mCallbacks.get());
1097 }
1098
1099 if (callbacks != null && count > 0) {
1100 callbacks.bindAllApplications(results);
1101 }
1102
1103 if (DEBUG_LOADERS) {
1104 Log.d(TAG, "bound " + count + " apps in "
1105 + (SystemClock.uptimeMillis() - t) + "ms");
1106 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001107 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001108 };
Joe Onoratobe386092009-11-17 17:32:16 -08001109
1110 public void dumpState() {
1111 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1112 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1113 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1114 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1115 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1116 }
1117 }
1118
1119 public void dumpState() {
1120 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1121 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1122 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1123 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1124 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1125 if (mLoaderThread != null) {
1126 mLoaderThread.dumpState();
1127 } else {
1128 Log.d(TAG, "mLoader.mLoaderThread=null");
1129 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001130 }
1131 }
1132
1133 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001134 * This is called from the code that adds shortcuts from the intent receiver. This
1135 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001136 */
Joe Onorato56d82912010-03-07 14:32:10 -05001137 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001138 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001139 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001140
Joe Onorato56d82912010-03-07 14:32:10 -05001141 /**
1142 * Make an ShortcutInfo object for a shortcut that is an application.
1143 *
1144 * If c is not null, then it will be used to fill in missing data like the title and icon.
1145 */
1146 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1147 Cursor c, int iconIndex, int titleIndex) {
1148 Bitmap icon = null;
1149 final ShortcutInfo info = new ShortcutInfo();
1150
1151 ComponentName componentName = intent.getComponent();
1152 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001153 return null;
1154 }
1155
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001156 // TODO: See if the PackageManager knows about this case. If it doesn't
1157 // then return null & delete this.
1158
Joe Onorato56d82912010-03-07 14:32:10 -05001159 // the resource -- This may implicitly give us back the fallback icon,
1160 // but don't worry about that. All we're doing with usingFallbackIcon is
1161 // to avoid saving lots of copies of that in the database, and most apps
1162 // have icons anyway.
1163 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1164 if (resolveInfo != null) {
1165 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001166 }
Joe Onorato56d82912010-03-07 14:32:10 -05001167 // the db
1168 if (icon == null) {
1169 if (c != null) {
1170 icon = getIconFromCursor(c, iconIndex);
1171 }
1172 }
1173 // the fallback icon
1174 if (icon == null) {
1175 icon = getFallbackIcon();
1176 info.usingFallbackIcon = true;
1177 }
1178 info.setIcon(icon);
1179
1180 // from the resource
1181 if (resolveInfo != null) {
1182 info.title = resolveInfo.activityInfo.loadLabel(manager);
1183 }
1184 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001185 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001186 if (c != null) {
1187 info.title = c.getString(titleIndex);
1188 }
1189 }
1190 // fall back to the class name of the activity
1191 if (info.title == null) {
1192 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001193 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001194 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1195 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001196 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001197
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001198 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001199 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001200 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001201 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001202 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1203 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001204
Joe Onorato56d82912010-03-07 14:32:10 -05001205 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001206 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001207 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001208
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001209 // TODO: If there's an explicit component and we can't install that, delete it.
1210
Joe Onorato56d82912010-03-07 14:32:10 -05001211 info.title = c.getString(titleIndex);
1212
Joe Onorato9c1289c2009-08-17 11:03:03 -04001213 int iconType = c.getInt(iconTypeIndex);
1214 switch (iconType) {
1215 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1216 String packageName = c.getString(iconPackageIndex);
1217 String resourceName = c.getString(iconResourceIndex);
1218 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001219 info.customIcon = false;
1220 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001221 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001222 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001223 if (resources != null) {
1224 final int id = resources.getIdentifier(resourceName, null, null);
1225 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1226 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001227 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001228 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001229 }
Joe Onorato56d82912010-03-07 14:32:10 -05001230 // the db
1231 if (icon == null) {
1232 icon = getIconFromCursor(c, iconIndex);
1233 }
1234 // the fallback icon
1235 if (icon == null) {
1236 icon = getFallbackIcon();
1237 info.usingFallbackIcon = true;
1238 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001239 break;
1240 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001241 icon = getIconFromCursor(c, iconIndex);
1242 if (icon == null) {
1243 icon = getFallbackIcon();
1244 info.customIcon = false;
1245 info.usingFallbackIcon = true;
1246 } else {
1247 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001248 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001249 break;
1250 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001251 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001252 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001253 info.customIcon = false;
1254 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001255 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001256 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001257 return info;
1258 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001259
Joe Onorato56d82912010-03-07 14:32:10 -05001260 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1261 if (false) {
1262 Log.d(TAG, "getIconFromCursor app="
1263 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1264 }
1265 byte[] data = c.getBlob(iconIndex);
1266 try {
1267 return BitmapFactory.decodeByteArray(data, 0, data.length);
1268 } catch (Exception e) {
1269 return null;
1270 }
1271 }
1272
Joe Onorato0589f0f2010-02-08 13:44:00 -08001273 ShortcutInfo addShortcut(Context context, Intent data,
1274 CellLayout.CellInfo cellInfo, boolean notify) {
1275
1276 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1277 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1278 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1279
1280 return info;
1281 }
1282
1283 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1284 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1285 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1286 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1287
1288 Bitmap icon = null;
1289 boolean filtered = false;
1290 boolean customIcon = false;
1291 ShortcutIconResource iconResource = null;
1292
1293 if (bitmap != null && bitmap instanceof Bitmap) {
1294 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1295 filtered = true;
1296 customIcon = true;
1297 } else {
1298 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1299 if (extra != null && extra instanceof ShortcutIconResource) {
1300 try {
1301 iconResource = (ShortcutIconResource) extra;
1302 final PackageManager packageManager = context.getPackageManager();
1303 Resources resources = packageManager.getResourcesForApplication(
1304 iconResource.packageName);
1305 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1306 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1307 } catch (Exception e) {
1308 Log.w(TAG, "Could not load shortcut icon: " + extra);
1309 }
1310 }
1311 }
1312
Joe Onorato0589f0f2010-02-08 13:44:00 -08001313 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001314
1315 if (icon == null) {
1316 icon = getFallbackIcon();
1317 info.usingFallbackIcon = true;
1318 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001319 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001320
Joe Onorato0589f0f2010-02-08 13:44:00 -08001321 info.title = name;
1322 info.intent = intent;
1323 info.customIcon = customIcon;
1324 info.iconResource = iconResource;
1325
1326 return info;
1327 }
1328
Joe Onorato9c1289c2009-08-17 11:03:03 -04001329 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1330 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1331
1332 int iconType = c.getInt(iconTypeIndex);
1333 switch (iconType) {
1334 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1335 String packageName = c.getString(iconPackageIndex);
1336 String resourceName = c.getString(iconResourceIndex);
1337 PackageManager packageManager = context.getPackageManager();
1338 try {
1339 Resources resources = packageManager.getResourcesForApplication(packageName);
1340 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001341 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1342 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001343 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001344 liveFolderInfo.icon = Utilities.createIconBitmap(
1345 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1346 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001347 }
1348 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1349 liveFolderInfo.iconResource.packageName = packageName;
1350 liveFolderInfo.iconResource.resourceName = resourceName;
1351 break;
1352 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001353 liveFolderInfo.icon = Utilities.createIconBitmap(
1354 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1355 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001356 }
1357 }
1358
Joe Onorato56d82912010-03-07 14:32:10 -05001359 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1360 // If this icon doesn't have a custom icon, check to see
1361 // what's stored in the DB, and if it doesn't match what
1362 // we're going to show, store what we are going to show back
1363 // into the DB. We do this so when we're loading, if the
1364 // package manager can't find an icon (for example because
1365 // the app is on SD) then we can use that instead.
1366 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1367 boolean needSave;
1368 byte[] data = c.getBlob(iconIndex);
1369 try {
1370 if (data != null) {
1371 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1372 Bitmap loaded = info.getIcon(mIconCache);
1373 needSave = !saved.sameAs(loaded);
1374 } else {
1375 needSave = true;
1376 }
1377 } catch (Exception e) {
1378 needSave = true;
1379 }
1380 if (needSave) {
1381 Log.d(TAG, "going to save icon bitmap for info=" + info);
1382 // This is slower than is ideal, but this only happens either
1383 // after the froyo OTA or when the app is updated with a new
1384 // icon.
1385 updateItemInDatabase(context, info);
1386 }
1387 }
1388 }
1389
Joe Onorato9c1289c2009-08-17 11:03:03 -04001390 /**
1391 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1392 * or make a new one.
1393 */
1394 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1395 // See if a placeholder was created for us already
1396 FolderInfo folderInfo = folders.get(id);
1397 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1398 // No placeholder -- create a new instance
1399 folderInfo = new UserFolderInfo();
1400 folders.put(id, folderInfo);
1401 }
1402 return (UserFolderInfo) folderInfo;
1403 }
1404
1405 /**
1406 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1407 * new one.
1408 */
1409 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1410 // See if a placeholder was created for us already
1411 FolderInfo folderInfo = folders.get(id);
1412 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1413 // No placeholder -- create a new instance
1414 folderInfo = new LiveFolderInfo();
1415 folders.put(id, folderInfo);
1416 }
1417 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001418 }
1419
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001420 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1421 String label = activityInfo.loadLabel(manager).toString();
1422 if (label == null) {
1423 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1424 if (label == null) {
1425 label = activityInfo.name;
1426 }
1427 }
1428 return label;
1429 }
1430
Joe Onorato9c1289c2009-08-17 11:03:03 -04001431 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001432 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001433 = new Comparator<ApplicationInfo>() {
1434 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1435 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001436 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001437 };
Joe Onoratobe386092009-11-17 17:32:16 -08001438
1439 public void dumpState() {
1440 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1441 Log.d(TAG, "mCallbacks=" + mCallbacks);
1442 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1443 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1444 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1445 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1446 mLoader.dumpState();
1447 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001448}