blob: de4f35b53ed27f6a5a6b67daa6c0b7ed3ee0544c [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
Joe Onoratof99f8c12009-10-31 17:27:36 -040019import android.content.BroadcastReceiver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080020import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.ContentValues;
23import android.content.Intent;
24import android.content.Context;
25import android.content.pm.ActivityInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.res.Resources;
29import android.database.Cursor;
30import android.graphics.Bitmap;
31import android.graphics.BitmapFactory;
32import android.net.Uri;
Joe Onorato9c1289c2009-08-17 11:03:03 -040033import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.os.Process;
Joe Onorato9c1289c2009-08-17 11:03:03 -040035import android.os.SystemClock;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036
Joe Onorato9c1289c2009-08-17 11:03:03 -040037import java.lang.ref.WeakReference;
38import java.net.URISyntaxException;
39import java.text.Collator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040import java.util.ArrayList;
Joe Onorato9c1289c2009-08-17 11:03:03 -040041import java.util.Comparator;
42import java.util.Collections;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043import java.util.HashMap;
44import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080045
46/**
47 * Maintains in-memory state of the Launcher. It is expected that there should be only one
48 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070049 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040051public class LauncherModel extends BroadcastReceiver {
Romain Guy829f56a2009-03-27 16:58:13 -070052 static final boolean DEBUG_LOADERS = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -040053 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070054
Joe Onoratof99f8c12009-10-31 17:27:36 -040055 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040056 private final Object mLock = new Object();
57 private DeferredHandler mHandler = new DeferredHandler();
58 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
Joe Onoratof99f8c12009-10-31 17:27:36 -040060 private boolean mBeforeFirstLoad = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -040061 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062
Joe Onorato9c1289c2009-08-17 11:03:03 -040063 private AllAppsList mAllAppsList = new AllAppsList();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
Joe Onorato9c1289c2009-08-17 11:03:03 -040065 public interface Callbacks {
66 public int getCurrentWorkspaceScreen();
67 public void startBinding();
68 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
69 public void finishBindingItems();
70 public void bindAppWidget(LauncherAppWidgetInfo info);
71 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
72 public void bindPackageAdded(ArrayList<ApplicationInfo> apps);
73 public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps);
74 public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps);
75 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080076
Joe Onoratof99f8c12009-10-31 17:27:36 -040077 LauncherModel(LauncherApplication app) {
78 mApp = app;
79 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080080
Joe Onorato9c1289c2009-08-17 11:03:03 -040081 /**
82 * Adds an item to the DB if it was not created previously, or move it to a new
83 * <container, screen, cellX, cellY>
84 */
85 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
86 int screen, int cellX, int cellY) {
87 if (item.container == ItemInfo.NO_ID) {
88 // From all apps
89 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
90 } else {
91 // From somewhere else
92 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080093 }
94 }
95
96 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -040097 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070098 */
Joe Onorato9c1289c2009-08-17 11:03:03 -040099 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
100 int cellX, int cellY) {
101 item.container = container;
102 item.screen = screen;
103 item.cellX = cellX;
104 item.cellY = cellY;
105
106 final ContentValues values = new ContentValues();
107 final ContentResolver cr = context.getContentResolver();
108
109 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
110 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
111 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
112 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
113
114 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700115 }
116
117 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400118 * Returns true if the shortcuts already exists in the database.
119 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400121 static boolean shortcutExists(Context context, String title, Intent intent) {
122 final ContentResolver cr = context.getContentResolver();
123 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
124 new String[] { "title", "intent" }, "title=? and intent=?",
125 new String[] { title, intent.toUri(0) }, null);
126 boolean result = false;
127 try {
128 result = c.moveToFirst();
129 } finally {
130 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400132 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700133 }
134
Joe Onorato9c1289c2009-08-17 11:03:03 -0400135 /**
136 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
137 */
138 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
139 final ContentResolver cr = context.getContentResolver();
140 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
141 "_id=? and (itemType=? or itemType=?)",
142 new String[] { String.valueOf(id),
143 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
144 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700145
Joe Onorato9c1289c2009-08-17 11:03:03 -0400146 try {
147 if (c.moveToFirst()) {
148 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
149 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
150 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
151 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
152 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
153 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800154
Joe Onorato9c1289c2009-08-17 11:03:03 -0400155 FolderInfo folderInfo = null;
156 switch (c.getInt(itemTypeIndex)) {
157 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
158 folderInfo = findOrMakeUserFolder(folderList, id);
159 break;
160 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
161 folderInfo = findOrMakeLiveFolder(folderList, id);
162 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700163 }
164
Joe Onorato9c1289c2009-08-17 11:03:03 -0400165 folderInfo.title = c.getString(titleIndex);
166 folderInfo.id = id;
167 folderInfo.container = c.getInt(containerIndex);
168 folderInfo.screen = c.getInt(screenIndex);
169 folderInfo.cellX = c.getInt(cellXIndex);
170 folderInfo.cellY = c.getInt(cellYIndex);
171
172 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700173 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400174 } finally {
175 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700176 }
177
178 return null;
179 }
180
Joe Onorato9c1289c2009-08-17 11:03:03 -0400181 /**
182 * Add an item to the database in a specified container. Sets the container, screen, cellX and
183 * cellY fields of the item. Also assigns an ID to the item.
184 */
185 static void addItemToDatabase(Context context, ItemInfo item, long container,
186 int screen, int cellX, int cellY, boolean notify) {
187 item.container = container;
188 item.screen = screen;
189 item.cellX = cellX;
190 item.cellY = cellY;
191
192 final ContentValues values = new ContentValues();
193 final ContentResolver cr = context.getContentResolver();
194
195 item.onAddToDatabase(values);
196
197 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
198 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
199
200 if (result != null) {
201 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700202 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700203 }
204
Joe Onorato9c1289c2009-08-17 11:03:03 -0400205 /**
206 * Update an item to the database in a specified container.
207 */
208 static void updateItemInDatabase(Context context, ItemInfo item) {
209 final ContentValues values = new ContentValues();
210 final ContentResolver cr = context.getContentResolver();
211
212 item.onAddToDatabase(values);
213
214 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
215 }
216
217 /**
218 * Removes the specified item from the database
219 * @param context
220 * @param item
221 */
222 static void deleteItemFromDatabase(Context context, ItemInfo item) {
223 final ContentResolver cr = context.getContentResolver();
224
225 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
226 }
227
228 /**
229 * Remove the contents of the specified folder from the database
230 */
231 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
232 final ContentResolver cr = context.getContentResolver();
233
234 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
235 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
236 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
237 }
238
239 /**
240 * Set this as the current Launcher activity object for the loader.
241 */
242 public void initialize(Callbacks callbacks) {
243 synchronized (mLock) {
244 mCallbacks = new WeakReference<Callbacks>(callbacks);
245 }
246 }
247
248 public void startLoader(Context context, boolean isLaunching) {
249 mLoader.startLoader(context, isLaunching);
250 }
251
252 public void stopLoader() {
253 mLoader.stopLoader();
254 }
255
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700256 /**
257 * We pick up most of the changes to all apps.
258 */
259 public void setAllAppsDirty() {
260 mLoader.setAllAppsDirty();
261 }
262
Joe Onorato9c1289c2009-08-17 11:03:03 -0400263 public void setWorkspaceDirty() {
264 mLoader.setWorkspaceDirty();
265 }
266
267 /**
268 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
269 * ACTION_PACKAGE_CHANGED.
270 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400271 public void onReceive(Context context, Intent intent) {
Joe Onorato0ace11a2009-11-05 15:41:27 -0500272 Log.d(TAG, "onReceive intent=" + intent);
273
Joe Onoratof99f8c12009-10-31 17:27:36 -0400274 // Use the app as the context.
275 context = mApp;
276
Joe Onorato9c1289c2009-08-17 11:03:03 -0400277 final String packageName = intent.getData().getSchemeSpecificPart();
278
279 ArrayList<ApplicationInfo> added = null;
280 ArrayList<ApplicationInfo> removed = null;
281 ArrayList<ApplicationInfo> modified = null;
282 boolean update = false;
283 boolean remove = false;
284
285 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400286 if (mBeforeFirstLoad) {
287 // If we haven't even loaded yet, don't bother, since we'll just pick
288 // up the changes.
289 return;
290 }
291
Joe Onorato9c1289c2009-08-17 11:03:03 -0400292 final String action = intent.getAction();
293 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
294
295 if (packageName == null || packageName.length() == 0) {
296 // they sent us a bad intent
297 return;
298 }
299
300 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
301 mAllAppsList.updatePackage(context, packageName);
302 update = true;
303 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
304 if (!replacing) {
305 mAllAppsList.removePackage(packageName);
306 remove = true;
307 }
308 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
309 // later, we will update the package at this time
310 } else {
311 if (!replacing) {
312 mAllAppsList.addPackage(context, packageName);
313 } else {
314 mAllAppsList.updatePackage(context, packageName);
315 update = true;
316 }
317 }
318
319 if (mAllAppsList.added.size() > 0) {
320 added = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800321 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400322 }
323 if (mAllAppsList.removed.size() > 0) {
324 removed = mAllAppsList.removed;
Romain Guy84f296c2009-11-04 15:00:44 -0800325 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400326 for (ApplicationInfo info: removed) {
327 AppInfoCache.remove(info.intent.getComponent());
328 }
329 }
330 if (mAllAppsList.modified.size() > 0) {
331 modified = mAllAppsList.modified;
Romain Guy84f296c2009-11-04 15:00:44 -0800332 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400333 }
334
Marco Nelissen3c8b90d2009-09-11 14:49:50 -0700335 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400336 if (callbacks == null) {
Joe Onorato0ace11a2009-11-05 15:41:27 -0500337 Log.d(TAG, "nobody to tell about the new app");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400338 return;
339 }
340
341 if (added != null) {
342 final ArrayList<ApplicationInfo> addedFinal = added;
343 mHandler.post(new Runnable() {
344 public void run() {
345 callbacks.bindPackageAdded(addedFinal);
346 }
347 });
348 }
349 if (update || modified != null) {
350 final ArrayList<ApplicationInfo> modifiedFinal = modified;
351 mHandler.post(new Runnable() {
352 public void run() {
353 callbacks.bindPackageUpdated(packageName, modifiedFinal);
354 }
355 });
356 }
357 if (remove || removed != null) {
358 final ArrayList<ApplicationInfo> removedFinal = removed;
359 mHandler.post(new Runnable() {
360 public void run() {
361 callbacks.bindPackageRemoved(packageName, removedFinal);
362 }
363 });
364 }
365 }
366 }
367
368 public class Loader {
369 private static final int ITEMS_CHUNK = 6;
370
371 private LoaderThread mLoaderThread;
372
373 private int mLastWorkspaceSeq = 0;
374 private int mWorkspaceSeq = 1;
375
376 private int mLastAllAppsSeq = 0;
377 private int mAllAppsSeq = 1;
378
Romain Guy84f296c2009-11-04 15:00:44 -0800379 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
380 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
381 final HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400382
383 /**
384 * Call this from the ui thread so the handler is initialized on the correct thread.
385 */
386 public Loader() {
387 }
388
389 public void startLoader(Context context, boolean isLaunching) {
390 synchronized (mLock) {
391 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
392 // Don't bother to start the thread if we know it's not going to do anything
393 if (mCallbacks.get() != null) {
394 LoaderThread oldThread = mLoaderThread;
395 if (oldThread != null) {
396 if (oldThread.isLaunching()) {
397 // don't downgrade isLaunching if we're already running
398 isLaunching = true;
399 }
400 oldThread.stopLocked();
401 }
402 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
403 mLoaderThread.start();
404 }
405 }
406 }
407
408 public void stopLoader() {
409 synchronized (mLock) {
410 if (mLoaderThread != null) {
411 mLoaderThread.stopLocked();
412 }
413 }
414 }
415
416 public void setWorkspaceDirty() {
417 synchronized (mLock) {
418 mWorkspaceSeq++;
419 }
420 }
421
422 public void setAllAppsDirty() {
423 synchronized (mLock) {
424 mAllAppsSeq++;
425 }
426 }
427
428 /**
429 * Runnable for the thread that loads the contents of the launcher:
430 * - workspace icons
431 * - widgets
432 * - all apps icons
433 */
434 private class LoaderThread extends Thread {
435 private Context mContext;
436 private Thread mWaitThread;
437 private boolean mIsLaunching;
438 private boolean mStopped;
439 private boolean mWorkspaceDoneBinding;
440
441 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
442 mContext = context;
443 mWaitThread = waitThread;
444 mIsLaunching = isLaunching;
445 }
446
447 boolean isLaunching() {
448 return mIsLaunching;
449 }
450
451 /**
452 * If another LoaderThread was supplied, we need to wait for that to finish before
453 * we start our processing. This keeps the ordering of the setting and clearing
454 * of the dirty flags correct by making sure we don't start processing stuff until
455 * they've had a chance to re-set them. We do this waiting the worker thread, not
456 * the ui thread to avoid ANRs.
457 */
458 private void waitForOtherThread() {
459 if (mWaitThread != null) {
460 boolean done = false;
461 while (!done) {
462 try {
463 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700464 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400465 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800466 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400467 }
468 }
469 mWaitThread = null;
470 }
471 }
472
473 public void run() {
474 waitForOtherThread();
475
476 // Elevate priority when Home launches for the first time to avoid
477 // starving at boot time. Staring at a blank home is not cool.
478 synchronized (mLock) {
479 android.os.Process.setThreadPriority(mIsLaunching
480 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
481 }
482
483 // Load the workspace only if it's dirty.
484 int workspaceSeq;
485 boolean workspaceDirty;
486 synchronized (mLock) {
487 workspaceSeq = mWorkspaceSeq;
488 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
489 }
490 if (workspaceDirty) {
491 loadWorkspace();
492 }
493 synchronized (mLock) {
494 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
495 if (mStopped) {
496 return;
497 }
498 if (workspaceSeq == mWorkspaceSeq) {
499 mLastWorkspaceSeq = mWorkspaceSeq;
500 }
501 }
502
503 // Bind the workspace
504 bindWorkspace();
505
506 // Wait until the either we're stopped or the other threads are done.
507 // This way we don't start loading all apps until the workspace has settled
508 // down.
509 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500510 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400511 public void run() {
512 synchronized (LoaderThread.this) {
513 mWorkspaceDoneBinding = true;
514 Log.d(TAG, "done with workspace");
515 LoaderThread.this.notify();
516 }
517 }
518 });
519 Log.d(TAG, "waiting to be done with workspace");
520 while (!mStopped && !mWorkspaceDoneBinding) {
521 try {
522 this.wait();
523 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800524 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400525 }
526 }
527 Log.d(TAG, "done waiting to be done with workspace");
528 }
529
530 // Load all apps if they're dirty
531 int allAppsSeq;
532 boolean allAppsDirty;
533 synchronized (mLock) {
534 allAppsSeq = mAllAppsSeq;
535 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratof99f8c12009-10-31 17:27:36 -0400536 //Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
537 // + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400538 }
539 if (allAppsDirty) {
540 loadAllApps();
541 }
542 synchronized (mLock) {
543 // If we're not stopped, and nobody has incremented mAllAppsSeq.
544 if (mStopped) {
545 return;
546 }
547 if (allAppsSeq == mAllAppsSeq) {
548 mLastAllAppsSeq = mAllAppsSeq;
549 }
550 }
551
552 // Bind all apps
Joe Onorato34b02492009-10-14 11:13:48 -0700553 if (allAppsDirty) {
554 bindAllApps();
555 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400556
557 // Clear out this reference, otherwise we end up holding it until all of the
558 // callback runnables are done.
559 mContext = null;
560
561 synchronized (mLock) {
562 // Setting the reference is atomic, but we can't do it inside the other critical
563 // sections.
564 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400565 }
566 }
567
568 public void stopLocked() {
569 synchronized (LoaderThread.this) {
570 mStopped = true;
571 this.notify();
572 }
573 }
574
575 /**
576 * Gets the callbacks object. If we've been stopped, or if the launcher object
577 * has somehow been garbage collected, return null instead.
578 */
579 Callbacks tryGetCallbacks() {
580 synchronized (mLock) {
581 if (mStopped) {
582 return null;
583 }
584
585 final Callbacks callbacks = mCallbacks.get();
586 if (callbacks == null) {
587 Log.w(TAG, "no mCallbacks");
588 return null;
589 }
590
591 return callbacks;
592 }
593 }
594
595 private void loadWorkspace() {
596 long t = SystemClock.uptimeMillis();
597
598 final Context context = mContext;
599 final ContentResolver contentResolver = context.getContentResolver();
600 final PackageManager manager = context.getPackageManager();
601
602 /* TODO
603 if (mLocaleChanged) {
604 updateShortcutLabels(contentResolver, manager);
605 }
606 */
607
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400608 mItems.clear();
609
Joe Onorato9c1289c2009-08-17 11:03:03 -0400610 final Cursor c = contentResolver.query(
611 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
612
613 try {
614 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
615 final int intentIndex = c.getColumnIndexOrThrow
616 (LauncherSettings.Favorites.INTENT);
617 final int titleIndex = c.getColumnIndexOrThrow
618 (LauncherSettings.Favorites.TITLE);
619 final int iconTypeIndex = c.getColumnIndexOrThrow(
620 LauncherSettings.Favorites.ICON_TYPE);
621 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
622 final int iconPackageIndex = c.getColumnIndexOrThrow(
623 LauncherSettings.Favorites.ICON_PACKAGE);
624 final int iconResourceIndex = c.getColumnIndexOrThrow(
625 LauncherSettings.Favorites.ICON_RESOURCE);
626 final int containerIndex = c.getColumnIndexOrThrow(
627 LauncherSettings.Favorites.CONTAINER);
628 final int itemTypeIndex = c.getColumnIndexOrThrow(
629 LauncherSettings.Favorites.ITEM_TYPE);
630 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
631 LauncherSettings.Favorites.APPWIDGET_ID);
632 final int screenIndex = c.getColumnIndexOrThrow(
633 LauncherSettings.Favorites.SCREEN);
634 final int cellXIndex = c.getColumnIndexOrThrow
635 (LauncherSettings.Favorites.CELLX);
636 final int cellYIndex = c.getColumnIndexOrThrow
637 (LauncherSettings.Favorites.CELLY);
638 final int spanXIndex = c.getColumnIndexOrThrow
639 (LauncherSettings.Favorites.SPANX);
640 final int spanYIndex = c.getColumnIndexOrThrow(
641 LauncherSettings.Favorites.SPANY);
642 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
643 final int displayModeIndex = c.getColumnIndexOrThrow(
644 LauncherSettings.Favorites.DISPLAY_MODE);
645
646 ApplicationInfo info;
647 String intentDescription;
648 Widget widgetInfo;
649 LauncherAppWidgetInfo appWidgetInfo;
650 int container;
651 long id;
652 Intent intent;
653
654 while (!mStopped && c.moveToNext()) {
655 try {
656 int itemType = c.getInt(itemTypeIndex);
657
658 switch (itemType) {
659 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
660 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
661 intentDescription = c.getString(intentIndex);
662 try {
663 intent = Intent.parseUri(intentDescription, 0);
664 } catch (URISyntaxException e) {
665 continue;
666 }
667
668 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
669 info = getApplicationInfo(manager, intent, context);
670 } else {
671 info = getApplicationInfoShortcut(c, context, iconTypeIndex,
672 iconPackageIndex, iconResourceIndex, iconIndex);
673 }
674
675 if (info == null) {
676 info = new ApplicationInfo();
677 info.icon = manager.getDefaultActivityIcon();
678 }
679
680 if (info != null) {
681 info.title = c.getString(titleIndex);
682 info.intent = intent;
683
684 info.id = c.getLong(idIndex);
685 container = c.getInt(containerIndex);
686 info.container = container;
687 info.screen = c.getInt(screenIndex);
688 info.cellX = c.getInt(cellXIndex);
689 info.cellY = c.getInt(cellYIndex);
690
691 switch (container) {
692 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
693 mItems.add(info);
694 break;
695 default:
696 // Item is in a user folder
697 UserFolderInfo folderInfo =
698 findOrMakeUserFolder(folders, container);
699 folderInfo.add(info);
700 break;
701 }
702 }
703 break;
704 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
705
706 id = c.getLong(idIndex);
707 UserFolderInfo folderInfo = findOrMakeUserFolder(folders, id);
708
709 folderInfo.title = c.getString(titleIndex);
710
711 folderInfo.id = id;
712 container = c.getInt(containerIndex);
713 folderInfo.container = container;
714 folderInfo.screen = c.getInt(screenIndex);
715 folderInfo.cellX = c.getInt(cellXIndex);
716 folderInfo.cellY = c.getInt(cellYIndex);
717
718 switch (container) {
719 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
720 mItems.add(folderInfo);
721 break;
722 }
723 break;
724 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
725
726 id = c.getLong(idIndex);
727 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(folders, id);
728
729 intentDescription = c.getString(intentIndex);
730 intent = null;
731 if (intentDescription != null) {
732 try {
733 intent = Intent.parseUri(intentDescription, 0);
734 } catch (URISyntaxException e) {
735 // Ignore, a live folder might not have a base intent
736 }
737 }
738
739 liveFolderInfo.title = c.getString(titleIndex);
740 liveFolderInfo.id = id;
741 container = c.getInt(containerIndex);
742 liveFolderInfo.container = container;
743 liveFolderInfo.screen = c.getInt(screenIndex);
744 liveFolderInfo.cellX = c.getInt(cellXIndex);
745 liveFolderInfo.cellY = c.getInt(cellYIndex);
746 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
747 liveFolderInfo.baseIntent = intent;
748 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
749
750 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
751 iconResourceIndex, liveFolderInfo);
752
753 switch (container) {
754 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
755 mItems.add(liveFolderInfo);
756 break;
757 }
758 break;
759 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
760 widgetInfo = Widget.makeSearch();
761
762 container = c.getInt(containerIndex);
763 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
764 Log.e(TAG, "Widget found where container "
765 + "!= CONTAINER_DESKTOP ignoring!");
766 continue;
767 }
768
769 widgetInfo.id = c.getLong(idIndex);
770 widgetInfo.screen = c.getInt(screenIndex);
771 widgetInfo.container = container;
772 widgetInfo.cellX = c.getInt(cellXIndex);
773 widgetInfo.cellY = c.getInt(cellYIndex);
774
775 mItems.add(widgetInfo);
776 break;
777 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
778 // Read all Launcher-specific widget details
779 int appWidgetId = c.getInt(appWidgetIdIndex);
780 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
781 appWidgetInfo.id = c.getLong(idIndex);
782 appWidgetInfo.screen = c.getInt(screenIndex);
783 appWidgetInfo.cellX = c.getInt(cellXIndex);
784 appWidgetInfo.cellY = c.getInt(cellYIndex);
785 appWidgetInfo.spanX = c.getInt(spanXIndex);
786 appWidgetInfo.spanY = c.getInt(spanYIndex);
787
788 container = c.getInt(containerIndex);
789 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
790 Log.e(TAG, "Widget found where container "
791 + "!= CONTAINER_DESKTOP -- ignoring!");
792 continue;
793 }
794 appWidgetInfo.container = c.getInt(containerIndex);
795
796 mAppWidgets.add(appWidgetInfo);
797 break;
798 }
799 } catch (Exception e) {
800 Log.w(TAG, "Desktop items loading interrupted:", e);
801 }
802 }
803 } finally {
804 c.close();
805 }
806 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
807 }
808
809 /**
810 * Read everything out of our database.
811 */
812 private void bindWorkspace() {
813 final long t = SystemClock.uptimeMillis();
814
815 // Don't use these two variables in any of the callback runnables.
816 // Otherwise we hold a reference to them.
817 Callbacks callbacks = mCallbacks.get();
818 if (callbacks == null) {
819 // This launcher has exited and nobody bothered to tell us. Just bail.
820 Log.w(TAG, "LoaderThread running with no launcher");
821 return;
822 }
823
824 int N;
825 // Tell the workspace that we're about to start firing items at it
826 mHandler.post(new Runnable() {
827 public void run() {
828 Callbacks callbacks = tryGetCallbacks();
829 if (callbacks != null) {
830 callbacks.startBinding();
831 }
832 }
833 });
834 // Add the items to the workspace.
835 N = mItems.size();
836 for (int i=0; i<N; i+=ITEMS_CHUNK) {
837 final int start = i;
838 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
839 mHandler.post(new Runnable() {
840 public void run() {
841 Callbacks callbacks = tryGetCallbacks();
842 if (callbacks != null) {
843 callbacks.bindItems(mItems, start, start+chunkSize);
844 }
845 }
846 });
847 }
848 // Wait until the queue goes empty.
849 mHandler.postIdle(new Runnable() {
850 public void run() {
851 Log.d(TAG, "Going to start binding widgets soon.");
852 }
853 });
854 // Bind the widgets, one at a time.
855 // WARNING: this is calling into the workspace from the background thread,
856 // but since getCurrentScreen() just returns the int, we should be okay. This
857 // is just a hint for the order, and if it's wrong, we'll be okay.
858 // TODO: instead, we should have that push the current screen into here.
859 final int currentScreen = callbacks.getCurrentWorkspaceScreen();
860 N = mAppWidgets.size();
861 // once for the current screen
862 for (int i=0; i<N; i++) {
863 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
864 if (widget.screen == currentScreen) {
865 mHandler.post(new Runnable() {
866 public void run() {
867 Callbacks callbacks = tryGetCallbacks();
868 if (callbacks != null) {
869 callbacks.bindAppWidget(widget);
870 }
871 }
872 });
873 }
874 }
875 // once for the other screens
876 for (int i=0; i<N; i++) {
877 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
878 if (widget.screen != currentScreen) {
879 mHandler.post(new Runnable() {
880 public void run() {
881 Callbacks callbacks = tryGetCallbacks();
882 if (callbacks != null) {
883 callbacks.bindAppWidget(widget);
884 }
885 }
886 });
887 }
888 }
889 // TODO: Bind the folders
890 // Tell the workspace that we're done.
891 mHandler.post(new Runnable() {
892 public void run() {
893 Callbacks callbacks = tryGetCallbacks();
894 if (callbacks != null) {
895 callbacks.finishBindingItems();
896 }
897 }
898 });
899 // If we're profiling, this is the last thing in the queue.
900 mHandler.post(new Runnable() {
901 public void run() {
902 Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
903 if (Launcher.PROFILE_ROTATE) {
904 android.os.Debug.stopMethodTracing();
905 }
906 }
907 });
908 }
909
910 private void loadAllApps() {
911 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
912 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
913
914 final Callbacks callbacks = tryGetCallbacks();
915 if (callbacks == null) {
916 return;
917 }
918
919 final Context context = mContext;
920 final PackageManager packageManager = context.getPackageManager();
921
922 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
923
924 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400925 mBeforeFirstLoad = false;
926
Joe Onorato9c1289c2009-08-17 11:03:03 -0400927 mAllAppsList.clear();
928 if (apps != null) {
929 long t = SystemClock.uptimeMillis();
930
931 int N = apps.size();
932 Utilities.BubbleText bubble = new Utilities.BubbleText(context);
933 for (int i=0; i<N && !mStopped; i++) {
934 // This builds the icon bitmaps.
935 mAllAppsList.add(AppInfoCache.cache(apps.get(i), context, bubble));
936 }
937 Collections.sort(mAllAppsList.data, sComparator);
938 Collections.sort(mAllAppsList.added, sComparator);
939 Log.d(TAG, "cached app icons in " + (SystemClock.uptimeMillis()-t) + "ms");
940 }
941 }
942 }
943
944 private void bindAllApps() {
945 synchronized (mLock) {
946 final ArrayList<ApplicationInfo> results = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800947 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400948 mHandler.post(new Runnable() {
949 public void run() {
Joe Onorato34b02492009-10-14 11:13:48 -0700950 final long t = SystemClock.uptimeMillis();
951 final int count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400952
953 Callbacks callbacks = tryGetCallbacks();
954 if (callbacks != null) {
955 callbacks.bindAllApplications(results);
956 }
957
Joe Onorato34b02492009-10-14 11:13:48 -0700958 Log.d(TAG, "bound app " + count + " icons in "
Joe Onorato9c1289c2009-08-17 11:03:03 -0400959 + (SystemClock.uptimeMillis()-t) + "ms");
960 }
961 });
962 }
963 }
964 }
965 }
966
967 /**
968 * Make an ApplicationInfo object for an application.
969 */
970 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
971 Context context) {
972 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
973
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700974 if (resolveInfo == null) {
975 return null;
976 }
977
Joe Onorato9c1289c2009-08-17 11:03:03 -0400978 final ApplicationInfo info = new ApplicationInfo();
979 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Joe Onorato6665c0f2009-09-02 15:27:24 -0700980 info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400981 if (info.title == null || info.title.length() == 0) {
982 info.title = activityInfo.loadLabel(manager);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700983 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400984 if (info.title == null) {
985 info.title = "";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700986 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400987 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
988 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800989 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700990
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800991 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400992 * Make an ApplicationInfo object for a sortcut
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800993 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400994 private static ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
995 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800996
Joe Onorato9c1289c2009-08-17 11:03:03 -0400997 final ApplicationInfo info = new ApplicationInfo();
998 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800999
Joe Onorato9c1289c2009-08-17 11:03:03 -04001000 int iconType = c.getInt(iconTypeIndex);
1001 switch (iconType) {
1002 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1003 String packageName = c.getString(iconPackageIndex);
1004 String resourceName = c.getString(iconResourceIndex);
1005 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001006 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001007 Resources resources = packageManager.getResourcesForApplication(packageName);
1008 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato6665c0f2009-09-02 15:27:24 -07001009 info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001010 } catch (Exception e) {
1011 info.icon = packageManager.getDefaultActivityIcon();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001012 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001013 info.iconResource = new Intent.ShortcutIconResource();
1014 info.iconResource.packageName = packageName;
1015 info.iconResource.resourceName = resourceName;
1016 info.customIcon = false;
1017 break;
1018 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1019 byte[] data = c.getBlob(iconIndex);
1020 try {
1021 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1022 info.icon = new FastBitmapDrawable(
1023 Utilities.createBitmapThumbnail(bitmap, context));
1024 } catch (Exception e) {
1025 packageManager = context.getPackageManager();
1026 info.icon = packageManager.getDefaultActivityIcon();
1027 }
1028 info.filtered = true;
1029 info.customIcon = true;
1030 break;
1031 default:
1032 info.icon = context.getPackageManager().getDefaultActivityIcon();
1033 info.customIcon = false;
1034 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001035 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001036 return info;
1037 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001038
Joe Onorato9c1289c2009-08-17 11:03:03 -04001039 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1040 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1041
1042 int iconType = c.getInt(iconTypeIndex);
1043 switch (iconType) {
1044 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1045 String packageName = c.getString(iconPackageIndex);
1046 String resourceName = c.getString(iconResourceIndex);
1047 PackageManager packageManager = context.getPackageManager();
1048 try {
1049 Resources resources = packageManager.getResourcesForApplication(packageName);
1050 final int id = resources.getIdentifier(resourceName, null, null);
1051 liveFolderInfo.icon = resources.getDrawable(id);
1052 } catch (Exception e) {
1053 liveFolderInfo.icon =
1054 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1055 }
1056 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1057 liveFolderInfo.iconResource.packageName = packageName;
1058 liveFolderInfo.iconResource.resourceName = resourceName;
1059 break;
1060 default:
1061 liveFolderInfo.icon =
1062 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1063 }
1064 }
1065
1066 /**
1067 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1068 * or make a new one.
1069 */
1070 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1071 // See if a placeholder was created for us already
1072 FolderInfo folderInfo = folders.get(id);
1073 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1074 // No placeholder -- create a new instance
1075 folderInfo = new UserFolderInfo();
1076 folders.put(id, folderInfo);
1077 }
1078 return (UserFolderInfo) folderInfo;
1079 }
1080
1081 /**
1082 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1083 * new one.
1084 */
1085 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1086 // See if a placeholder was created for us already
1087 FolderInfo folderInfo = folders.get(id);
1088 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1089 // No placeholder -- create a new instance
1090 folderInfo = new LiveFolderInfo();
1091 folders.put(id, folderInfo);
1092 }
1093 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001094 }
1095
1096 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
1097 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -07001098 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001099 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
1100 null, null, null);
1101
Romain Guy73b979d2009-06-09 12:57:21 -07001102 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001103 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1104 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1105 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1106
1107 // boolean changed = false;
1108
1109 try {
1110 while (c.moveToNext()) {
1111 try {
1112 if (c.getInt(itemTypeIndex) !=
1113 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1114 continue;
1115 }
1116
1117 final String intentUri = c.getString(intentIndex);
1118 if (intentUri != null) {
Romain Guy1ce1a242009-06-23 17:34:54 -07001119 final Intent shortcut = Intent.parseUri(intentUri, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001120 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
1121 final ComponentName name = shortcut.getComponent();
1122 if (name != null) {
1123 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
1124 final String title = c.getString(titleIndex);
1125 String label = getLabel(manager, activityInfo);
1126
1127 if (title == null || !title.equals(label)) {
1128 final ContentValues values = new ContentValues();
1129 values.put(LauncherSettings.Favorites.TITLE, label);
1130
Romain Guyfedc4fc2009-03-27 20:48:20 -07001131 resolver.update(
1132 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001133 values, "_id=?",
1134 new String[] { String.valueOf(c.getLong(idIndex)) });
1135
1136 // changed = true;
1137 }
1138 }
1139 }
1140 }
1141 } catch (URISyntaxException e) {
1142 // Ignore
1143 } catch (PackageManager.NameNotFoundException e) {
1144 // Ignore
1145 }
1146 }
1147 } finally {
1148 c.close();
1149 }
1150
1151 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
1152 }
1153
1154 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1155 String label = activityInfo.loadLabel(manager).toString();
1156 if (label == null) {
1157 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1158 if (label == null) {
1159 label = activityInfo.name;
1160 }
1161 }
1162 return label;
1163 }
1164
Joe Onorato9c1289c2009-08-17 11:03:03 -04001165 private static final Collator sCollator = Collator.getInstance();
1166 private static final Comparator<ApplicationInfo> sComparator
1167 = new Comparator<ApplicationInfo>() {
1168 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1169 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001170 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001171 };
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001172}