blob: fc97832a5119ccc4ca60a949a4820dd87f9f14af [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);
Joe Onoratoad72e172009-11-06 16:25:04 -050069 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040070 public void finishBindingItems();
71 public void bindAppWidget(LauncherAppWidgetInfo info);
72 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
73 public void bindPackageAdded(ArrayList<ApplicationInfo> apps);
74 public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps);
75 public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps);
76 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077
Joe Onoratof99f8c12009-10-31 17:27:36 -040078 LauncherModel(LauncherApplication app) {
79 mApp = app;
80 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081
Joe Onorato9c1289c2009-08-17 11:03:03 -040082 /**
83 * Adds an item to the DB if it was not created previously, or move it to a new
84 * <container, screen, cellX, cellY>
85 */
86 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
87 int screen, int cellX, int cellY) {
88 if (item.container == ItemInfo.NO_ID) {
89 // From all apps
90 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
91 } else {
92 // From somewhere else
93 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094 }
95 }
96
97 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -040098 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070099 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400100 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
101 int cellX, int cellY) {
102 item.container = container;
103 item.screen = screen;
104 item.cellX = cellX;
105 item.cellY = cellY;
106
107 final ContentValues values = new ContentValues();
108 final ContentResolver cr = context.getContentResolver();
109
110 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
111 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
112 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
113 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
114
115 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700116 }
117
118 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400119 * Returns true if the shortcuts already exists in the database.
120 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400122 static boolean shortcutExists(Context context, String title, Intent intent) {
123 final ContentResolver cr = context.getContentResolver();
124 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
125 new String[] { "title", "intent" }, "title=? and intent=?",
126 new String[] { title, intent.toUri(0) }, null);
127 boolean result = false;
128 try {
129 result = c.moveToFirst();
130 } finally {
131 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800132 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400133 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700134 }
135
Joe Onorato9c1289c2009-08-17 11:03:03 -0400136 /**
137 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
138 */
139 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
140 final ContentResolver cr = context.getContentResolver();
141 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
142 "_id=? and (itemType=? or itemType=?)",
143 new String[] { String.valueOf(id),
144 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
145 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700146
Joe Onorato9c1289c2009-08-17 11:03:03 -0400147 try {
148 if (c.moveToFirst()) {
149 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
150 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
151 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
152 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
153 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
154 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800155
Joe Onorato9c1289c2009-08-17 11:03:03 -0400156 FolderInfo folderInfo = null;
157 switch (c.getInt(itemTypeIndex)) {
158 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
159 folderInfo = findOrMakeUserFolder(folderList, id);
160 break;
161 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
162 folderInfo = findOrMakeLiveFolder(folderList, id);
163 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700164 }
165
Joe Onorato9c1289c2009-08-17 11:03:03 -0400166 folderInfo.title = c.getString(titleIndex);
167 folderInfo.id = id;
168 folderInfo.container = c.getInt(containerIndex);
169 folderInfo.screen = c.getInt(screenIndex);
170 folderInfo.cellX = c.getInt(cellXIndex);
171 folderInfo.cellY = c.getInt(cellYIndex);
172
173 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700174 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400175 } finally {
176 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700177 }
178
179 return null;
180 }
181
Joe Onorato9c1289c2009-08-17 11:03:03 -0400182 /**
183 * Add an item to the database in a specified container. Sets the container, screen, cellX and
184 * cellY fields of the item. Also assigns an ID to the item.
185 */
186 static void addItemToDatabase(Context context, ItemInfo item, long container,
187 int screen, int cellX, int cellY, boolean notify) {
188 item.container = container;
189 item.screen = screen;
190 item.cellX = cellX;
191 item.cellY = cellY;
192
193 final ContentValues values = new ContentValues();
194 final ContentResolver cr = context.getContentResolver();
195
196 item.onAddToDatabase(values);
197
198 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
199 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
200
201 if (result != null) {
202 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700203 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700204 }
205
Joe Onorato9c1289c2009-08-17 11:03:03 -0400206 /**
207 * Update an item to the database in a specified container.
208 */
209 static void updateItemInDatabase(Context context, ItemInfo item) {
210 final ContentValues values = new ContentValues();
211 final ContentResolver cr = context.getContentResolver();
212
213 item.onAddToDatabase(values);
214
215 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
216 }
217
218 /**
219 * Removes the specified item from the database
220 * @param context
221 * @param item
222 */
223 static void deleteItemFromDatabase(Context context, ItemInfo item) {
224 final ContentResolver cr = context.getContentResolver();
225
226 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
227 }
228
229 /**
230 * Remove the contents of the specified folder from the database
231 */
232 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
233 final ContentResolver cr = context.getContentResolver();
234
235 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
236 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
237 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
238 }
239
240 /**
241 * Set this as the current Launcher activity object for the loader.
242 */
243 public void initialize(Callbacks callbacks) {
244 synchronized (mLock) {
245 mCallbacks = new WeakReference<Callbacks>(callbacks);
246 }
247 }
248
249 public void startLoader(Context context, boolean isLaunching) {
250 mLoader.startLoader(context, isLaunching);
251 }
252
253 public void stopLoader() {
254 mLoader.stopLoader();
255 }
256
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700257 /**
258 * We pick up most of the changes to all apps.
259 */
260 public void setAllAppsDirty() {
261 mLoader.setAllAppsDirty();
262 }
263
Joe Onorato9c1289c2009-08-17 11:03:03 -0400264 public void setWorkspaceDirty() {
265 mLoader.setWorkspaceDirty();
266 }
267
268 /**
269 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
270 * ACTION_PACKAGE_CHANGED.
271 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400272 public void onReceive(Context context, Intent intent) {
Joe Onorato0ace11a2009-11-05 15:41:27 -0500273 Log.d(TAG, "onReceive intent=" + intent);
274
Joe Onoratof99f8c12009-10-31 17:27:36 -0400275 // Use the app as the context.
276 context = mApp;
277
Joe Onorato9c1289c2009-08-17 11:03:03 -0400278 final String packageName = intent.getData().getSchemeSpecificPart();
279
280 ArrayList<ApplicationInfo> added = null;
281 ArrayList<ApplicationInfo> removed = null;
282 ArrayList<ApplicationInfo> modified = null;
283 boolean update = false;
284 boolean remove = false;
285
286 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400287 if (mBeforeFirstLoad) {
288 // If we haven't even loaded yet, don't bother, since we'll just pick
289 // up the changes.
290 return;
291 }
292
Joe Onorato9c1289c2009-08-17 11:03:03 -0400293 final String action = intent.getAction();
294 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
295
296 if (packageName == null || packageName.length() == 0) {
297 // they sent us a bad intent
298 return;
299 }
300
301 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
302 mAllAppsList.updatePackage(context, packageName);
303 update = true;
304 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
305 if (!replacing) {
306 mAllAppsList.removePackage(packageName);
307 remove = true;
308 }
309 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
310 // later, we will update the package at this time
311 } else {
312 if (!replacing) {
313 mAllAppsList.addPackage(context, packageName);
314 } else {
315 mAllAppsList.updatePackage(context, packageName);
316 update = true;
317 }
318 }
319
320 if (mAllAppsList.added.size() > 0) {
321 added = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800322 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400323 }
324 if (mAllAppsList.removed.size() > 0) {
325 removed = mAllAppsList.removed;
Romain Guy84f296c2009-11-04 15:00:44 -0800326 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400327 for (ApplicationInfo info: removed) {
328 AppInfoCache.remove(info.intent.getComponent());
329 }
330 }
331 if (mAllAppsList.modified.size() > 0) {
332 modified = mAllAppsList.modified;
Romain Guy84f296c2009-11-04 15:00:44 -0800333 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400334 }
335
Marco Nelissen3c8b90d2009-09-11 14:49:50 -0700336 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400337 if (callbacks == null) {
Joe Onorato0ace11a2009-11-05 15:41:27 -0500338 Log.d(TAG, "nobody to tell about the new app");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400339 return;
340 }
341
342 if (added != null) {
343 final ArrayList<ApplicationInfo> addedFinal = added;
344 mHandler.post(new Runnable() {
345 public void run() {
346 callbacks.bindPackageAdded(addedFinal);
347 }
348 });
349 }
350 if (update || modified != null) {
351 final ArrayList<ApplicationInfo> modifiedFinal = modified;
352 mHandler.post(new Runnable() {
353 public void run() {
354 callbacks.bindPackageUpdated(packageName, modifiedFinal);
355 }
356 });
357 }
358 if (remove || removed != null) {
359 final ArrayList<ApplicationInfo> removedFinal = removed;
360 mHandler.post(new Runnable() {
361 public void run() {
362 callbacks.bindPackageRemoved(packageName, removedFinal);
363 }
364 });
365 }
366 }
367 }
368
369 public class Loader {
370 private static final int ITEMS_CHUNK = 6;
371
372 private LoaderThread mLoaderThread;
373
374 private int mLastWorkspaceSeq = 0;
375 private int mWorkspaceSeq = 1;
376
377 private int mLastAllAppsSeq = 0;
378 private int mAllAppsSeq = 1;
379
Romain Guy84f296c2009-11-04 15:00:44 -0800380 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
381 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500382 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400383
384 /**
385 * Call this from the ui thread so the handler is initialized on the correct thread.
386 */
387 public Loader() {
388 }
389
390 public void startLoader(Context context, boolean isLaunching) {
391 synchronized (mLock) {
392 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
393 // Don't bother to start the thread if we know it's not going to do anything
394 if (mCallbacks.get() != null) {
395 LoaderThread oldThread = mLoaderThread;
396 if (oldThread != null) {
397 if (oldThread.isLaunching()) {
398 // don't downgrade isLaunching if we're already running
399 isLaunching = true;
400 }
401 oldThread.stopLocked();
402 }
403 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
404 mLoaderThread.start();
405 }
406 }
407 }
408
409 public void stopLoader() {
410 synchronized (mLock) {
411 if (mLoaderThread != null) {
412 mLoaderThread.stopLocked();
413 }
414 }
415 }
416
417 public void setWorkspaceDirty() {
418 synchronized (mLock) {
419 mWorkspaceSeq++;
420 }
421 }
422
423 public void setAllAppsDirty() {
424 synchronized (mLock) {
425 mAllAppsSeq++;
426 }
427 }
428
429 /**
430 * Runnable for the thread that loads the contents of the launcher:
431 * - workspace icons
432 * - widgets
433 * - all apps icons
434 */
435 private class LoaderThread extends Thread {
436 private Context mContext;
437 private Thread mWaitThread;
438 private boolean mIsLaunching;
439 private boolean mStopped;
440 private boolean mWorkspaceDoneBinding;
441
442 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
443 mContext = context;
444 mWaitThread = waitThread;
445 mIsLaunching = isLaunching;
446 }
447
448 boolean isLaunching() {
449 return mIsLaunching;
450 }
451
452 /**
453 * If another LoaderThread was supplied, we need to wait for that to finish before
454 * we start our processing. This keeps the ordering of the setting and clearing
455 * of the dirty flags correct by making sure we don't start processing stuff until
456 * they've had a chance to re-set them. We do this waiting the worker thread, not
457 * the ui thread to avoid ANRs.
458 */
459 private void waitForOtherThread() {
460 if (mWaitThread != null) {
461 boolean done = false;
462 while (!done) {
463 try {
464 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700465 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400466 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800467 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400468 }
469 }
470 mWaitThread = null;
471 }
472 }
473
474 public void run() {
475 waitForOtherThread();
476
477 // Elevate priority when Home launches for the first time to avoid
478 // starving at boot time. Staring at a blank home is not cool.
479 synchronized (mLock) {
480 android.os.Process.setThreadPriority(mIsLaunching
481 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
482 }
483
484 // Load the workspace only if it's dirty.
485 int workspaceSeq;
486 boolean workspaceDirty;
487 synchronized (mLock) {
488 workspaceSeq = mWorkspaceSeq;
489 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
490 }
491 if (workspaceDirty) {
492 loadWorkspace();
493 }
494 synchronized (mLock) {
495 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
496 if (mStopped) {
497 return;
498 }
499 if (workspaceSeq == mWorkspaceSeq) {
500 mLastWorkspaceSeq = mWorkspaceSeq;
501 }
502 }
503
504 // Bind the workspace
505 bindWorkspace();
506
507 // Wait until the either we're stopped or the other threads are done.
508 // This way we don't start loading all apps until the workspace has settled
509 // down.
510 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500511 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400512 public void run() {
513 synchronized (LoaderThread.this) {
514 mWorkspaceDoneBinding = true;
515 Log.d(TAG, "done with workspace");
516 LoaderThread.this.notify();
517 }
518 }
519 });
520 Log.d(TAG, "waiting to be done with workspace");
521 while (!mStopped && !mWorkspaceDoneBinding) {
522 try {
523 this.wait();
524 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800525 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400526 }
527 }
528 Log.d(TAG, "done waiting to be done with workspace");
529 }
530
531 // Load all apps if they're dirty
532 int allAppsSeq;
533 boolean allAppsDirty;
534 synchronized (mLock) {
535 allAppsSeq = mAllAppsSeq;
536 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratof99f8c12009-10-31 17:27:36 -0400537 //Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
538 // + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400539 }
540 if (allAppsDirty) {
541 loadAllApps();
542 }
543 synchronized (mLock) {
544 // If we're not stopped, and nobody has incremented mAllAppsSeq.
545 if (mStopped) {
546 return;
547 }
548 if (allAppsSeq == mAllAppsSeq) {
549 mLastAllAppsSeq = mAllAppsSeq;
550 }
551 }
552
553 // Bind all apps
Joe Onorato34b02492009-10-14 11:13:48 -0700554 if (allAppsDirty) {
555 bindAllApps();
556 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400557
558 // Clear out this reference, otherwise we end up holding it until all of the
559 // callback runnables are done.
560 mContext = null;
561
562 synchronized (mLock) {
563 // Setting the reference is atomic, but we can't do it inside the other critical
564 // sections.
565 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400566 }
567 }
568
569 public void stopLocked() {
570 synchronized (LoaderThread.this) {
571 mStopped = true;
572 this.notify();
573 }
574 }
575
576 /**
577 * Gets the callbacks object. If we've been stopped, or if the launcher object
578 * has somehow been garbage collected, return null instead.
579 */
580 Callbacks tryGetCallbacks() {
581 synchronized (mLock) {
582 if (mStopped) {
583 return null;
584 }
585
586 final Callbacks callbacks = mCallbacks.get();
587 if (callbacks == null) {
588 Log.w(TAG, "no mCallbacks");
589 return null;
590 }
591
592 return callbacks;
593 }
594 }
595
596 private void loadWorkspace() {
597 long t = SystemClock.uptimeMillis();
598
599 final Context context = mContext;
600 final ContentResolver contentResolver = context.getContentResolver();
601 final PackageManager manager = context.getPackageManager();
602
603 /* TODO
604 if (mLocaleChanged) {
605 updateShortcutLabels(contentResolver, manager);
606 }
607 */
608
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400609 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500610 mAppWidgets.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400611
Joe Onorato9c1289c2009-08-17 11:03:03 -0400612 final Cursor c = contentResolver.query(
613 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
614
615 try {
616 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
617 final int intentIndex = c.getColumnIndexOrThrow
618 (LauncherSettings.Favorites.INTENT);
619 final int titleIndex = c.getColumnIndexOrThrow
620 (LauncherSettings.Favorites.TITLE);
621 final int iconTypeIndex = c.getColumnIndexOrThrow(
622 LauncherSettings.Favorites.ICON_TYPE);
623 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
624 final int iconPackageIndex = c.getColumnIndexOrThrow(
625 LauncherSettings.Favorites.ICON_PACKAGE);
626 final int iconResourceIndex = c.getColumnIndexOrThrow(
627 LauncherSettings.Favorites.ICON_RESOURCE);
628 final int containerIndex = c.getColumnIndexOrThrow(
629 LauncherSettings.Favorites.CONTAINER);
630 final int itemTypeIndex = c.getColumnIndexOrThrow(
631 LauncherSettings.Favorites.ITEM_TYPE);
632 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
633 LauncherSettings.Favorites.APPWIDGET_ID);
634 final int screenIndex = c.getColumnIndexOrThrow(
635 LauncherSettings.Favorites.SCREEN);
636 final int cellXIndex = c.getColumnIndexOrThrow
637 (LauncherSettings.Favorites.CELLX);
638 final int cellYIndex = c.getColumnIndexOrThrow
639 (LauncherSettings.Favorites.CELLY);
640 final int spanXIndex = c.getColumnIndexOrThrow
641 (LauncherSettings.Favorites.SPANX);
642 final int spanYIndex = c.getColumnIndexOrThrow(
643 LauncherSettings.Favorites.SPANY);
644 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
645 final int displayModeIndex = c.getColumnIndexOrThrow(
646 LauncherSettings.Favorites.DISPLAY_MODE);
647
648 ApplicationInfo info;
649 String intentDescription;
650 Widget widgetInfo;
651 LauncherAppWidgetInfo appWidgetInfo;
652 int container;
653 long id;
654 Intent intent;
655
656 while (!mStopped && c.moveToNext()) {
657 try {
658 int itemType = c.getInt(itemTypeIndex);
659
660 switch (itemType) {
661 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
662 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
663 intentDescription = c.getString(intentIndex);
664 try {
665 intent = Intent.parseUri(intentDescription, 0);
666 } catch (URISyntaxException e) {
667 continue;
668 }
669
670 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
671 info = getApplicationInfo(manager, intent, context);
672 } else {
673 info = getApplicationInfoShortcut(c, context, iconTypeIndex,
674 iconPackageIndex, iconResourceIndex, iconIndex);
675 }
676
677 if (info == null) {
678 info = new ApplicationInfo();
679 info.icon = manager.getDefaultActivityIcon();
680 }
681
682 if (info != null) {
Joe Onorato028b6242009-11-10 18:26:13 -0800683 if (itemType
684 != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
685 info.title = c.getString(titleIndex);
686 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400687 info.intent = intent;
688
689 info.id = c.getLong(idIndex);
690 container = c.getInt(containerIndex);
691 info.container = container;
692 info.screen = c.getInt(screenIndex);
693 info.cellX = c.getInt(cellXIndex);
694 info.cellY = c.getInt(cellYIndex);
695
696 switch (container) {
697 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
698 mItems.add(info);
699 break;
700 default:
701 // Item is in a user folder
702 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500703 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400704 folderInfo.add(info);
705 break;
706 }
707 }
708 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400709
Joe Onoratoad72e172009-11-06 16:25:04 -0500710 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400711 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500712 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400713
714 folderInfo.title = c.getString(titleIndex);
715
716 folderInfo.id = id;
717 container = c.getInt(containerIndex);
718 folderInfo.container = container;
719 folderInfo.screen = c.getInt(screenIndex);
720 folderInfo.cellX = c.getInt(cellXIndex);
721 folderInfo.cellY = c.getInt(cellYIndex);
722
723 switch (container) {
724 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
725 mItems.add(folderInfo);
726 break;
727 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500728
729 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400730 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500731
Joe Onorato9c1289c2009-08-17 11:03:03 -0400732 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
733
734 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500735 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400736
737 intentDescription = c.getString(intentIndex);
738 intent = null;
739 if (intentDescription != null) {
740 try {
741 intent = Intent.parseUri(intentDescription, 0);
742 } catch (URISyntaxException e) {
743 // Ignore, a live folder might not have a base intent
744 }
745 }
746
747 liveFolderInfo.title = c.getString(titleIndex);
748 liveFolderInfo.id = id;
749 container = c.getInt(containerIndex);
750 liveFolderInfo.container = container;
751 liveFolderInfo.screen = c.getInt(screenIndex);
752 liveFolderInfo.cellX = c.getInt(cellXIndex);
753 liveFolderInfo.cellY = c.getInt(cellYIndex);
754 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
755 liveFolderInfo.baseIntent = intent;
756 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
757
758 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
759 iconResourceIndex, liveFolderInfo);
760
761 switch (container) {
762 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
763 mItems.add(liveFolderInfo);
764 break;
765 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500766 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400767 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500768
Joe Onorato9c1289c2009-08-17 11:03:03 -0400769 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
770 widgetInfo = Widget.makeSearch();
771
772 container = c.getInt(containerIndex);
773 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
774 Log.e(TAG, "Widget found where container "
775 + "!= CONTAINER_DESKTOP ignoring!");
776 continue;
777 }
778
779 widgetInfo.id = c.getLong(idIndex);
780 widgetInfo.screen = c.getInt(screenIndex);
781 widgetInfo.container = container;
782 widgetInfo.cellX = c.getInt(cellXIndex);
783 widgetInfo.cellY = c.getInt(cellYIndex);
784
785 mItems.add(widgetInfo);
786 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500787
Joe Onorato9c1289c2009-08-17 11:03:03 -0400788 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
789 // Read all Launcher-specific widget details
790 int appWidgetId = c.getInt(appWidgetIdIndex);
791 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
792 appWidgetInfo.id = c.getLong(idIndex);
793 appWidgetInfo.screen = c.getInt(screenIndex);
794 appWidgetInfo.cellX = c.getInt(cellXIndex);
795 appWidgetInfo.cellY = c.getInt(cellYIndex);
796 appWidgetInfo.spanX = c.getInt(spanXIndex);
797 appWidgetInfo.spanY = c.getInt(spanYIndex);
798
799 container = c.getInt(containerIndex);
800 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
801 Log.e(TAG, "Widget found where container "
802 + "!= CONTAINER_DESKTOP -- ignoring!");
803 continue;
804 }
805 appWidgetInfo.container = c.getInt(containerIndex);
806
807 mAppWidgets.add(appWidgetInfo);
808 break;
809 }
810 } catch (Exception e) {
811 Log.w(TAG, "Desktop items loading interrupted:", e);
812 }
813 }
814 } finally {
815 c.close();
816 }
817 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
818 }
819
820 /**
821 * Read everything out of our database.
822 */
823 private void bindWorkspace() {
824 final long t = SystemClock.uptimeMillis();
825
826 // Don't use these two variables in any of the callback runnables.
827 // Otherwise we hold a reference to them.
828 Callbacks callbacks = mCallbacks.get();
829 if (callbacks == null) {
830 // This launcher has exited and nobody bothered to tell us. Just bail.
831 Log.w(TAG, "LoaderThread running with no launcher");
832 return;
833 }
834
835 int N;
836 // Tell the workspace that we're about to start firing items at it
837 mHandler.post(new Runnable() {
838 public void run() {
839 Callbacks callbacks = tryGetCallbacks();
840 if (callbacks != null) {
841 callbacks.startBinding();
842 }
843 }
844 });
845 // Add the items to the workspace.
846 N = mItems.size();
847 for (int i=0; i<N; i+=ITEMS_CHUNK) {
848 final int start = i;
849 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
850 mHandler.post(new Runnable() {
851 public void run() {
852 Callbacks callbacks = tryGetCallbacks();
853 if (callbacks != null) {
854 callbacks.bindItems(mItems, start, start+chunkSize);
855 }
856 }
857 });
858 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500859 mHandler.post(new Runnable() {
860 public void run() {
861 Callbacks callbacks = tryGetCallbacks();
862 if (callbacks != null) {
863 callbacks.bindFolders(mFolders);
864 }
865 }
866 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400867 // Wait until the queue goes empty.
868 mHandler.postIdle(new Runnable() {
869 public void run() {
870 Log.d(TAG, "Going to start binding widgets soon.");
871 }
872 });
873 // Bind the widgets, one at a time.
874 // WARNING: this is calling into the workspace from the background thread,
875 // but since getCurrentScreen() just returns the int, we should be okay. This
876 // is just a hint for the order, and if it's wrong, we'll be okay.
877 // TODO: instead, we should have that push the current screen into here.
878 final int currentScreen = callbacks.getCurrentWorkspaceScreen();
879 N = mAppWidgets.size();
880 // once for the current screen
881 for (int i=0; i<N; i++) {
882 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
883 if (widget.screen == currentScreen) {
884 mHandler.post(new Runnable() {
885 public void run() {
886 Callbacks callbacks = tryGetCallbacks();
887 if (callbacks != null) {
888 callbacks.bindAppWidget(widget);
889 }
890 }
891 });
892 }
893 }
894 // once for the other screens
895 for (int i=0; i<N; i++) {
896 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
897 if (widget.screen != currentScreen) {
898 mHandler.post(new Runnable() {
899 public void run() {
900 Callbacks callbacks = tryGetCallbacks();
901 if (callbacks != null) {
902 callbacks.bindAppWidget(widget);
903 }
904 }
905 });
906 }
907 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400908 // Tell the workspace that we're done.
909 mHandler.post(new Runnable() {
910 public void run() {
911 Callbacks callbacks = tryGetCallbacks();
912 if (callbacks != null) {
913 callbacks.finishBindingItems();
914 }
915 }
916 });
917 // If we're profiling, this is the last thing in the queue.
918 mHandler.post(new Runnable() {
919 public void run() {
920 Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
921 if (Launcher.PROFILE_ROTATE) {
922 android.os.Debug.stopMethodTracing();
923 }
924 }
925 });
926 }
927
928 private void loadAllApps() {
929 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
930 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
931
932 final Callbacks callbacks = tryGetCallbacks();
933 if (callbacks == null) {
934 return;
935 }
936
937 final Context context = mContext;
938 final PackageManager packageManager = context.getPackageManager();
939
940 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
941
942 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400943 mBeforeFirstLoad = false;
944
Joe Onorato9c1289c2009-08-17 11:03:03 -0400945 mAllAppsList.clear();
946 if (apps != null) {
947 long t = SystemClock.uptimeMillis();
948
949 int N = apps.size();
950 Utilities.BubbleText bubble = new Utilities.BubbleText(context);
951 for (int i=0; i<N && !mStopped; i++) {
952 // This builds the icon bitmaps.
953 mAllAppsList.add(AppInfoCache.cache(apps.get(i), context, bubble));
954 }
955 Collections.sort(mAllAppsList.data, sComparator);
956 Collections.sort(mAllAppsList.added, sComparator);
957 Log.d(TAG, "cached app icons in " + (SystemClock.uptimeMillis()-t) + "ms");
958 }
959 }
960 }
961
962 private void bindAllApps() {
963 synchronized (mLock) {
964 final ArrayList<ApplicationInfo> results = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800965 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400966 mHandler.post(new Runnable() {
967 public void run() {
Joe Onorato34b02492009-10-14 11:13:48 -0700968 final long t = SystemClock.uptimeMillis();
969 final int count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400970
971 Callbacks callbacks = tryGetCallbacks();
972 if (callbacks != null) {
973 callbacks.bindAllApplications(results);
974 }
975
Joe Onorato34b02492009-10-14 11:13:48 -0700976 Log.d(TAG, "bound app " + count + " icons in "
Joe Onorato9c1289c2009-08-17 11:03:03 -0400977 + (SystemClock.uptimeMillis()-t) + "ms");
978 }
979 });
980 }
981 }
982 }
983 }
984
985 /**
986 * Make an ApplicationInfo object for an application.
987 */
988 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
989 Context context) {
990 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
991
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700992 if (resolveInfo == null) {
993 return null;
994 }
995
Joe Onorato9c1289c2009-08-17 11:03:03 -0400996 final ApplicationInfo info = new ApplicationInfo();
997 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Joe Onorato6665c0f2009-09-02 15:27:24 -0700998 info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400999 if (info.title == null || info.title.length() == 0) {
1000 info.title = activityInfo.loadLabel(manager);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001001 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001002 if (info.title == null) {
1003 info.title = "";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001004 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001005 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1006 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001007 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001008
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001010 * Make an ApplicationInfo object for a sortcut
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001011 */
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012 private static ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
1013 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001014
Joe Onorato9c1289c2009-08-17 11:03:03 -04001015 final ApplicationInfo info = new ApplicationInfo();
1016 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001017
Joe Onorato9c1289c2009-08-17 11:03:03 -04001018 int iconType = c.getInt(iconTypeIndex);
1019 switch (iconType) {
1020 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1021 String packageName = c.getString(iconPackageIndex);
1022 String resourceName = c.getString(iconResourceIndex);
1023 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001024 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001025 Resources resources = packageManager.getResourcesForApplication(packageName);
1026 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato6665c0f2009-09-02 15:27:24 -07001027 info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001028 } catch (Exception e) {
1029 info.icon = packageManager.getDefaultActivityIcon();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001030 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001031 info.iconResource = new Intent.ShortcutIconResource();
1032 info.iconResource.packageName = packageName;
1033 info.iconResource.resourceName = resourceName;
1034 info.customIcon = false;
1035 break;
1036 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1037 byte[] data = c.getBlob(iconIndex);
1038 try {
1039 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1040 info.icon = new FastBitmapDrawable(
1041 Utilities.createBitmapThumbnail(bitmap, context));
1042 } catch (Exception e) {
1043 packageManager = context.getPackageManager();
1044 info.icon = packageManager.getDefaultActivityIcon();
1045 }
1046 info.filtered = true;
1047 info.customIcon = true;
1048 break;
1049 default:
1050 info.icon = context.getPackageManager().getDefaultActivityIcon();
1051 info.customIcon = false;
1052 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001053 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001054 return info;
1055 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001056
Joe Onorato9c1289c2009-08-17 11:03:03 -04001057 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1058 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1059
1060 int iconType = c.getInt(iconTypeIndex);
1061 switch (iconType) {
1062 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1063 String packageName = c.getString(iconPackageIndex);
1064 String resourceName = c.getString(iconResourceIndex);
1065 PackageManager packageManager = context.getPackageManager();
1066 try {
1067 Resources resources = packageManager.getResourcesForApplication(packageName);
1068 final int id = resources.getIdentifier(resourceName, null, null);
1069 liveFolderInfo.icon = resources.getDrawable(id);
1070 } catch (Exception e) {
1071 liveFolderInfo.icon =
1072 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1073 }
1074 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1075 liveFolderInfo.iconResource.packageName = packageName;
1076 liveFolderInfo.iconResource.resourceName = resourceName;
1077 break;
1078 default:
1079 liveFolderInfo.icon =
1080 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1081 }
1082 }
1083
1084 /**
1085 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1086 * or make a new one.
1087 */
1088 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1089 // See if a placeholder was created for us already
1090 FolderInfo folderInfo = folders.get(id);
1091 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1092 // No placeholder -- create a new instance
1093 folderInfo = new UserFolderInfo();
1094 folders.put(id, folderInfo);
1095 }
1096 return (UserFolderInfo) folderInfo;
1097 }
1098
1099 /**
1100 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1101 * new one.
1102 */
1103 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1104 // See if a placeholder was created for us already
1105 FolderInfo folderInfo = folders.get(id);
1106 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1107 // No placeholder -- create a new instance
1108 folderInfo = new LiveFolderInfo();
1109 folders.put(id, folderInfo);
1110 }
1111 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001112 }
1113
1114 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
1115 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -07001116 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001117 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
1118 null, null, null);
1119
Romain Guy73b979d2009-06-09 12:57:21 -07001120 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001121 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1122 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1123 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1124
1125 // boolean changed = false;
1126
1127 try {
1128 while (c.moveToNext()) {
1129 try {
1130 if (c.getInt(itemTypeIndex) !=
1131 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1132 continue;
1133 }
1134
1135 final String intentUri = c.getString(intentIndex);
1136 if (intentUri != null) {
Romain Guy1ce1a242009-06-23 17:34:54 -07001137 final Intent shortcut = Intent.parseUri(intentUri, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001138 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
1139 final ComponentName name = shortcut.getComponent();
1140 if (name != null) {
1141 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
1142 final String title = c.getString(titleIndex);
1143 String label = getLabel(manager, activityInfo);
1144
1145 if (title == null || !title.equals(label)) {
1146 final ContentValues values = new ContentValues();
1147 values.put(LauncherSettings.Favorites.TITLE, label);
1148
Romain Guyfedc4fc2009-03-27 20:48:20 -07001149 resolver.update(
1150 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001151 values, "_id=?",
1152 new String[] { String.valueOf(c.getLong(idIndex)) });
1153
1154 // changed = true;
1155 }
1156 }
1157 }
1158 }
1159 } catch (URISyntaxException e) {
1160 // Ignore
1161 } catch (PackageManager.NameNotFoundException e) {
1162 // Ignore
1163 }
1164 }
1165 } finally {
1166 c.close();
1167 }
1168
1169 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
1170 }
1171
1172 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1173 String label = activityInfo.loadLabel(manager).toString();
1174 if (label == null) {
1175 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1176 if (label == null) {
1177 label = activityInfo.name;
1178 }
1179 }
1180 return label;
1181 }
1182
Joe Onorato9c1289c2009-08-17 11:03:03 -04001183 private static final Collator sCollator = Collator.getInstance();
1184 private static final Comparator<ApplicationInfo> sComparator
1185 = new Comparator<ApplicationInfo>() {
1186 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1187 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001188 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001189 };
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001190}