blob: d76e02605ce96277d16792882cd09d7f6229e143 [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) {
683 info.title = c.getString(titleIndex);
684 info.intent = intent;
685
686 info.id = c.getLong(idIndex);
687 container = c.getInt(containerIndex);
688 info.container = container;
689 info.screen = c.getInt(screenIndex);
690 info.cellX = c.getInt(cellXIndex);
691 info.cellY = c.getInt(cellYIndex);
692
693 switch (container) {
694 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
695 mItems.add(info);
696 break;
697 default:
698 // Item is in a user folder
699 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500700 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400701 folderInfo.add(info);
702 break;
703 }
704 }
705 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400706
Joe Onoratoad72e172009-11-06 16:25:04 -0500707 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400708 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500709 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400710
711 folderInfo.title = c.getString(titleIndex);
712
713 folderInfo.id = id;
714 container = c.getInt(containerIndex);
715 folderInfo.container = container;
716 folderInfo.screen = c.getInt(screenIndex);
717 folderInfo.cellX = c.getInt(cellXIndex);
718 folderInfo.cellY = c.getInt(cellYIndex);
719
720 switch (container) {
721 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
722 mItems.add(folderInfo);
723 break;
724 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500725
726 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400727 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500728
Joe Onorato9c1289c2009-08-17 11:03:03 -0400729 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
730
731 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500732 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400733
734 intentDescription = c.getString(intentIndex);
735 intent = null;
736 if (intentDescription != null) {
737 try {
738 intent = Intent.parseUri(intentDescription, 0);
739 } catch (URISyntaxException e) {
740 // Ignore, a live folder might not have a base intent
741 }
742 }
743
744 liveFolderInfo.title = c.getString(titleIndex);
745 liveFolderInfo.id = id;
746 container = c.getInt(containerIndex);
747 liveFolderInfo.container = container;
748 liveFolderInfo.screen = c.getInt(screenIndex);
749 liveFolderInfo.cellX = c.getInt(cellXIndex);
750 liveFolderInfo.cellY = c.getInt(cellYIndex);
751 liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
752 liveFolderInfo.baseIntent = intent;
753 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
754
755 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
756 iconResourceIndex, liveFolderInfo);
757
758 switch (container) {
759 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
760 mItems.add(liveFolderInfo);
761 break;
762 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500763 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400764 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500765
Joe Onorato9c1289c2009-08-17 11:03:03 -0400766 case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
767 widgetInfo = Widget.makeSearch();
768
769 container = c.getInt(containerIndex);
770 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
771 Log.e(TAG, "Widget found where container "
772 + "!= CONTAINER_DESKTOP ignoring!");
773 continue;
774 }
775
776 widgetInfo.id = c.getLong(idIndex);
777 widgetInfo.screen = c.getInt(screenIndex);
778 widgetInfo.container = container;
779 widgetInfo.cellX = c.getInt(cellXIndex);
780 widgetInfo.cellY = c.getInt(cellYIndex);
781
782 mItems.add(widgetInfo);
783 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500784
Joe Onorato9c1289c2009-08-17 11:03:03 -0400785 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
786 // Read all Launcher-specific widget details
787 int appWidgetId = c.getInt(appWidgetIdIndex);
788 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
789 appWidgetInfo.id = c.getLong(idIndex);
790 appWidgetInfo.screen = c.getInt(screenIndex);
791 appWidgetInfo.cellX = c.getInt(cellXIndex);
792 appWidgetInfo.cellY = c.getInt(cellYIndex);
793 appWidgetInfo.spanX = c.getInt(spanXIndex);
794 appWidgetInfo.spanY = c.getInt(spanYIndex);
795
796 container = c.getInt(containerIndex);
797 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
798 Log.e(TAG, "Widget found where container "
799 + "!= CONTAINER_DESKTOP -- ignoring!");
800 continue;
801 }
802 appWidgetInfo.container = c.getInt(containerIndex);
803
804 mAppWidgets.add(appWidgetInfo);
805 break;
806 }
807 } catch (Exception e) {
808 Log.w(TAG, "Desktop items loading interrupted:", e);
809 }
810 }
811 } finally {
812 c.close();
813 }
814 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
815 }
816
817 /**
818 * Read everything out of our database.
819 */
820 private void bindWorkspace() {
821 final long t = SystemClock.uptimeMillis();
822
823 // Don't use these two variables in any of the callback runnables.
824 // Otherwise we hold a reference to them.
825 Callbacks callbacks = mCallbacks.get();
826 if (callbacks == null) {
827 // This launcher has exited and nobody bothered to tell us. Just bail.
828 Log.w(TAG, "LoaderThread running with no launcher");
829 return;
830 }
831
832 int N;
833 // Tell the workspace that we're about to start firing items at it
834 mHandler.post(new Runnable() {
835 public void run() {
836 Callbacks callbacks = tryGetCallbacks();
837 if (callbacks != null) {
838 callbacks.startBinding();
839 }
840 }
841 });
842 // Add the items to the workspace.
843 N = mItems.size();
844 for (int i=0; i<N; i+=ITEMS_CHUNK) {
845 final int start = i;
846 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
847 mHandler.post(new Runnable() {
848 public void run() {
849 Callbacks callbacks = tryGetCallbacks();
850 if (callbacks != null) {
851 callbacks.bindItems(mItems, start, start+chunkSize);
852 }
853 }
854 });
855 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500856 mHandler.post(new Runnable() {
857 public void run() {
858 Callbacks callbacks = tryGetCallbacks();
859 if (callbacks != null) {
860 callbacks.bindFolders(mFolders);
861 }
862 }
863 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400864 // Wait until the queue goes empty.
865 mHandler.postIdle(new Runnable() {
866 public void run() {
867 Log.d(TAG, "Going to start binding widgets soon.");
868 }
869 });
870 // Bind the widgets, one at a time.
871 // WARNING: this is calling into the workspace from the background thread,
872 // but since getCurrentScreen() just returns the int, we should be okay. This
873 // is just a hint for the order, and if it's wrong, we'll be okay.
874 // TODO: instead, we should have that push the current screen into here.
875 final int currentScreen = callbacks.getCurrentWorkspaceScreen();
876 N = mAppWidgets.size();
877 // once for the current screen
878 for (int i=0; i<N; i++) {
879 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
880 if (widget.screen == currentScreen) {
881 mHandler.post(new Runnable() {
882 public void run() {
883 Callbacks callbacks = tryGetCallbacks();
884 if (callbacks != null) {
885 callbacks.bindAppWidget(widget);
886 }
887 }
888 });
889 }
890 }
891 // once for the other screens
892 for (int i=0; i<N; i++) {
893 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
894 if (widget.screen != currentScreen) {
895 mHandler.post(new Runnable() {
896 public void run() {
897 Callbacks callbacks = tryGetCallbacks();
898 if (callbacks != null) {
899 callbacks.bindAppWidget(widget);
900 }
901 }
902 });
903 }
904 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400905 // Tell the workspace that we're done.
906 mHandler.post(new Runnable() {
907 public void run() {
908 Callbacks callbacks = tryGetCallbacks();
909 if (callbacks != null) {
910 callbacks.finishBindingItems();
911 }
912 }
913 });
914 // If we're profiling, this is the last thing in the queue.
915 mHandler.post(new Runnable() {
916 public void run() {
917 Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
918 if (Launcher.PROFILE_ROTATE) {
919 android.os.Debug.stopMethodTracing();
920 }
921 }
922 });
923 }
924
925 private void loadAllApps() {
926 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
927 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
928
929 final Callbacks callbacks = tryGetCallbacks();
930 if (callbacks == null) {
931 return;
932 }
933
934 final Context context = mContext;
935 final PackageManager packageManager = context.getPackageManager();
936
937 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
938
939 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400940 mBeforeFirstLoad = false;
941
Joe Onorato9c1289c2009-08-17 11:03:03 -0400942 mAllAppsList.clear();
943 if (apps != null) {
944 long t = SystemClock.uptimeMillis();
945
946 int N = apps.size();
947 Utilities.BubbleText bubble = new Utilities.BubbleText(context);
948 for (int i=0; i<N && !mStopped; i++) {
949 // This builds the icon bitmaps.
950 mAllAppsList.add(AppInfoCache.cache(apps.get(i), context, bubble));
951 }
952 Collections.sort(mAllAppsList.data, sComparator);
953 Collections.sort(mAllAppsList.added, sComparator);
954 Log.d(TAG, "cached app icons in " + (SystemClock.uptimeMillis()-t) + "ms");
955 }
956 }
957 }
958
959 private void bindAllApps() {
960 synchronized (mLock) {
961 final ArrayList<ApplicationInfo> results = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800962 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400963 mHandler.post(new Runnable() {
964 public void run() {
Joe Onorato34b02492009-10-14 11:13:48 -0700965 final long t = SystemClock.uptimeMillis();
966 final int count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400967
968 Callbacks callbacks = tryGetCallbacks();
969 if (callbacks != null) {
970 callbacks.bindAllApplications(results);
971 }
972
Joe Onorato34b02492009-10-14 11:13:48 -0700973 Log.d(TAG, "bound app " + count + " icons in "
Joe Onorato9c1289c2009-08-17 11:03:03 -0400974 + (SystemClock.uptimeMillis()-t) + "ms");
975 }
976 });
977 }
978 }
979 }
980 }
981
982 /**
983 * Make an ApplicationInfo object for an application.
984 */
985 private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent,
986 Context context) {
987 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
988
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700989 if (resolveInfo == null) {
990 return null;
991 }
992
Joe Onorato9c1289c2009-08-17 11:03:03 -0400993 final ApplicationInfo info = new ApplicationInfo();
994 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Joe Onorato6665c0f2009-09-02 15:27:24 -0700995 info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400996 if (info.title == null || info.title.length() == 0) {
997 info.title = activityInfo.loadLabel(manager);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700998 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400999 if (info.title == null) {
1000 info.title = "";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001001 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001002 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1003 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001004 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001005
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001006 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001007 * Make an ApplicationInfo object for a sortcut
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001008 */
Joe Onorato9c1289c2009-08-17 11:03:03 -04001009 private static ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context,
1010 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001011
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012 final ApplicationInfo info = new ApplicationInfo();
1013 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001014
Joe Onorato9c1289c2009-08-17 11:03:03 -04001015 int iconType = c.getInt(iconTypeIndex);
1016 switch (iconType) {
1017 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1018 String packageName = c.getString(iconPackageIndex);
1019 String resourceName = c.getString(iconResourceIndex);
1020 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001021 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001022 Resources resources = packageManager.getResourcesForApplication(packageName);
1023 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato6665c0f2009-09-02 15:27:24 -07001024 info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001025 } catch (Exception e) {
1026 info.icon = packageManager.getDefaultActivityIcon();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001027 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001028 info.iconResource = new Intent.ShortcutIconResource();
1029 info.iconResource.packageName = packageName;
1030 info.iconResource.resourceName = resourceName;
1031 info.customIcon = false;
1032 break;
1033 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1034 byte[] data = c.getBlob(iconIndex);
1035 try {
1036 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
1037 info.icon = new FastBitmapDrawable(
1038 Utilities.createBitmapThumbnail(bitmap, context));
1039 } catch (Exception e) {
1040 packageManager = context.getPackageManager();
1041 info.icon = packageManager.getDefaultActivityIcon();
1042 }
1043 info.filtered = true;
1044 info.customIcon = true;
1045 break;
1046 default:
1047 info.icon = context.getPackageManager().getDefaultActivityIcon();
1048 info.customIcon = false;
1049 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001050 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001051 return info;
1052 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001053
Joe Onorato9c1289c2009-08-17 11:03:03 -04001054 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1055 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1056
1057 int iconType = c.getInt(iconTypeIndex);
1058 switch (iconType) {
1059 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1060 String packageName = c.getString(iconPackageIndex);
1061 String resourceName = c.getString(iconResourceIndex);
1062 PackageManager packageManager = context.getPackageManager();
1063 try {
1064 Resources resources = packageManager.getResourcesForApplication(packageName);
1065 final int id = resources.getIdentifier(resourceName, null, null);
1066 liveFolderInfo.icon = resources.getDrawable(id);
1067 } catch (Exception e) {
1068 liveFolderInfo.icon =
1069 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1070 }
1071 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1072 liveFolderInfo.iconResource.packageName = packageName;
1073 liveFolderInfo.iconResource.resourceName = resourceName;
1074 break;
1075 default:
1076 liveFolderInfo.icon =
1077 context.getResources().getDrawable(R.drawable.ic_launcher_folder);
1078 }
1079 }
1080
1081 /**
1082 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1083 * or make a new one.
1084 */
1085 private static UserFolderInfo findOrMakeUserFolder(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 UserFolderInfo)) {
1089 // No placeholder -- create a new instance
1090 folderInfo = new UserFolderInfo();
1091 folders.put(id, folderInfo);
1092 }
1093 return (UserFolderInfo) folderInfo;
1094 }
1095
1096 /**
1097 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1098 * new one.
1099 */
1100 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1101 // See if a placeholder was created for us already
1102 FolderInfo folderInfo = folders.get(id);
1103 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1104 // No placeholder -- create a new instance
1105 folderInfo = new LiveFolderInfo();
1106 folders.put(id, folderInfo);
1107 }
1108 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001109 }
1110
1111 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
1112 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -07001113 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001114 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
1115 null, null, null);
1116
Romain Guy73b979d2009-06-09 12:57:21 -07001117 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001118 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1119 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1120 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1121
1122 // boolean changed = false;
1123
1124 try {
1125 while (c.moveToNext()) {
1126 try {
1127 if (c.getInt(itemTypeIndex) !=
1128 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1129 continue;
1130 }
1131
1132 final String intentUri = c.getString(intentIndex);
1133 if (intentUri != null) {
Romain Guy1ce1a242009-06-23 17:34:54 -07001134 final Intent shortcut = Intent.parseUri(intentUri, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001135 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
1136 final ComponentName name = shortcut.getComponent();
1137 if (name != null) {
1138 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
1139 final String title = c.getString(titleIndex);
1140 String label = getLabel(manager, activityInfo);
1141
1142 if (title == null || !title.equals(label)) {
1143 final ContentValues values = new ContentValues();
1144 values.put(LauncherSettings.Favorites.TITLE, label);
1145
Romain Guyfedc4fc2009-03-27 20:48:20 -07001146 resolver.update(
1147 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001148 values, "_id=?",
1149 new String[] { String.valueOf(c.getLong(idIndex)) });
1150
1151 // changed = true;
1152 }
1153 }
1154 }
1155 }
1156 } catch (URISyntaxException e) {
1157 // Ignore
1158 } catch (PackageManager.NameNotFoundException e) {
1159 // Ignore
1160 }
1161 }
1162 } finally {
1163 c.close();
1164 }
1165
1166 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
1167 }
1168
1169 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1170 String label = activityInfo.loadLabel(manager).toString();
1171 if (label == null) {
1172 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1173 if (label == null) {
1174 label = activityInfo.name;
1175 }
1176 }
1177 return label;
1178 }
1179
Joe Onorato9c1289c2009-08-17 11:03:03 -04001180 private static final Collator sCollator = Collator.getInstance();
1181 private static final Comparator<ApplicationInfo> sComparator
1182 = new Comparator<ApplicationInfo>() {
1183 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1184 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001185 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001186 };
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001187}