blob: 16e5c3b6ff407a7ccfdcaefebd641df9fd43e5bd [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Romain Guy629de3e2010-01-13 12:20:59 -080019import android.appwidget.AppWidgetManager;
20import android.appwidget.AppWidgetProviderInfo;
Joe Onoratof99f8c12009-10-31 17:27:36 -040021import android.content.BroadcastReceiver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080022import android.content.ComponentName;
Romain Guy5c16f3e2010-01-12 17:24:58 -080023import android.content.ContentProviderClient;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080027import android.content.Intent.ShortcutIconResource;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.Context;
29import android.content.pm.ActivityInfo;
30import android.content.pm.PackageManager;
Romain Guy5c16f3e2010-01-12 17:24:58 -080031import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.content.pm.ResolveInfo;
33import android.content.res.Resources;
34import android.database.Cursor;
35import android.graphics.Bitmap;
36import android.graphics.BitmapFactory;
37import android.net.Uri;
Joe Onorato0589f0f2010-02-08 13:44:00 -080038import android.os.Parcelable;
Romain Guy5c16f3e2010-01-12 17:24:58 -080039import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040040import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041import android.os.Process;
Joe Onorato9c1289c2009-08-17 11:03:03 -040042import android.os.SystemClock;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043
Joe Onorato9c1289c2009-08-17 11:03:03 -040044import java.lang.ref.WeakReference;
45import java.net.URISyntaxException;
46import java.text.Collator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import java.util.ArrayList;
Joe Onorato9c1289c2009-08-17 11:03:03 -040048import java.util.Comparator;
49import java.util.Collections;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import java.util.HashMap;
51import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052
53/**
54 * Maintains in-memory state of the Launcher. It is expected that there should be only one
55 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070056 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040058public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080059 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040060 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070061
Joe Onoratof99f8c12009-10-31 17:27:36 -040062 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040063 private final Object mLock = new Object();
64 private DeferredHandler mHandler = new DeferredHandler();
65 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080066
Joe Onoratof99f8c12009-10-31 17:27:36 -040067 private boolean mBeforeFirstLoad = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -040068 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069
Joe Onorato0589f0f2010-02-08 13:44:00 -080070 private AllAppsList mAllAppsList;
71 private IconCache mIconCache;
72
73 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080074
Joe Onorato9c1289c2009-08-17 11:03:03 -040075 public interface Callbacks {
76 public int getCurrentWorkspaceScreen();
77 public void startBinding();
78 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050079 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040080 public void finishBindingItems();
81 public void bindAppWidget(LauncherAppWidgetInfo info);
82 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
83 public void bindPackageAdded(ArrayList<ApplicationInfo> apps);
84 public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps);
85 public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps);
86 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080087
Joe Onorato0589f0f2010-02-08 13:44:00 -080088 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -040089 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -080090 mAllAppsList = new AllAppsList(iconCache);
91 mIconCache = iconCache;
92
93 mDefaultIcon = Utilities.createIconBitmap(
94 app.getPackageManager().getDefaultActivityIcon(), app);
95 }
96
97 public Bitmap getDefaultIcon() {
98 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -040099 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100
Joe Onorato9c1289c2009-08-17 11:03:03 -0400101 /**
102 * Adds an item to the DB if it was not created previously, or move it to a new
103 * <container, screen, cellX, cellY>
104 */
105 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
106 int screen, int cellX, int cellY) {
107 if (item.container == ItemInfo.NO_ID) {
108 // From all apps
109 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
110 } else {
111 // From somewhere else
112 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113 }
114 }
115
116 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400117 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700118 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400119 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
120 int cellX, int cellY) {
121 item.container = container;
122 item.screen = screen;
123 item.cellX = cellX;
124 item.cellY = cellY;
125
126 final ContentValues values = new ContentValues();
127 final ContentResolver cr = context.getContentResolver();
128
129 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
130 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
131 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
132 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
133
134 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700135 }
136
137 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400138 * Returns true if the shortcuts already exists in the database.
139 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800140 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400141 static boolean shortcutExists(Context context, String title, Intent intent) {
142 final ContentResolver cr = context.getContentResolver();
143 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
144 new String[] { "title", "intent" }, "title=? and intent=?",
145 new String[] { title, intent.toUri(0) }, null);
146 boolean result = false;
147 try {
148 result = c.moveToFirst();
149 } finally {
150 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800151 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400152 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700153 }
154
Joe Onorato9c1289c2009-08-17 11:03:03 -0400155 /**
156 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
157 */
158 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
159 final ContentResolver cr = context.getContentResolver();
160 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
161 "_id=? and (itemType=? or itemType=?)",
162 new String[] { String.valueOf(id),
163 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
164 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700165
Joe Onorato9c1289c2009-08-17 11:03:03 -0400166 try {
167 if (c.moveToFirst()) {
168 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
169 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
170 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
171 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
172 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
173 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800174
Joe Onorato9c1289c2009-08-17 11:03:03 -0400175 FolderInfo folderInfo = null;
176 switch (c.getInt(itemTypeIndex)) {
177 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
178 folderInfo = findOrMakeUserFolder(folderList, id);
179 break;
180 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
181 folderInfo = findOrMakeLiveFolder(folderList, id);
182 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700183 }
184
Joe Onorato9c1289c2009-08-17 11:03:03 -0400185 folderInfo.title = c.getString(titleIndex);
186 folderInfo.id = id;
187 folderInfo.container = c.getInt(containerIndex);
188 folderInfo.screen = c.getInt(screenIndex);
189 folderInfo.cellX = c.getInt(cellXIndex);
190 folderInfo.cellY = c.getInt(cellYIndex);
191
192 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700193 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400194 } finally {
195 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700196 }
197
198 return null;
199 }
200
Joe Onorato9c1289c2009-08-17 11:03:03 -0400201 /**
202 * Add an item to the database in a specified container. Sets the container, screen, cellX and
203 * cellY fields of the item. Also assigns an ID to the item.
204 */
205 static void addItemToDatabase(Context context, ItemInfo item, long container,
206 int screen, int cellX, int cellY, boolean notify) {
207 item.container = container;
208 item.screen = screen;
209 item.cellX = cellX;
210 item.cellY = cellY;
211
212 final ContentValues values = new ContentValues();
213 final ContentResolver cr = context.getContentResolver();
214
215 item.onAddToDatabase(values);
216
217 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
218 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
219
220 if (result != null) {
221 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700222 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700223 }
224
Joe Onorato9c1289c2009-08-17 11:03:03 -0400225 /**
226 * Update an item to the database in a specified container.
227 */
228 static void updateItemInDatabase(Context context, ItemInfo item) {
229 final ContentValues values = new ContentValues();
230 final ContentResolver cr = context.getContentResolver();
231
232 item.onAddToDatabase(values);
233
234 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
235 }
236
237 /**
238 * Removes the specified item from the database
239 * @param context
240 * @param item
241 */
242 static void deleteItemFromDatabase(Context context, ItemInfo item) {
243 final ContentResolver cr = context.getContentResolver();
244
245 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
246 }
247
248 /**
249 * Remove the contents of the specified folder from the database
250 */
251 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
252 final ContentResolver cr = context.getContentResolver();
253
254 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
255 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
256 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
257 }
258
259 /**
260 * Set this as the current Launcher activity object for the loader.
261 */
262 public void initialize(Callbacks callbacks) {
263 synchronized (mLock) {
264 mCallbacks = new WeakReference<Callbacks>(callbacks);
265 }
266 }
267
268 public void startLoader(Context context, boolean isLaunching) {
269 mLoader.startLoader(context, isLaunching);
270 }
271
272 public void stopLoader() {
273 mLoader.stopLoader();
274 }
275
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700276 /**
277 * We pick up most of the changes to all apps.
278 */
279 public void setAllAppsDirty() {
280 mLoader.setAllAppsDirty();
281 }
282
Joe Onorato9c1289c2009-08-17 11:03:03 -0400283 public void setWorkspaceDirty() {
284 mLoader.setWorkspaceDirty();
285 }
286
287 /**
288 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
289 * ACTION_PACKAGE_CHANGED.
290 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400291 public void onReceive(Context context, Intent intent) {
292 // Use the app as the context.
293 context = mApp;
294
Joe Onorato9c1289c2009-08-17 11:03:03 -0400295 final String packageName = intent.getData().getSchemeSpecificPart();
296
297 ArrayList<ApplicationInfo> added = null;
298 ArrayList<ApplicationInfo> removed = null;
299 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400300
301 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400302 if (mBeforeFirstLoad) {
303 // If we haven't even loaded yet, don't bother, since we'll just pick
304 // up the changes.
305 return;
306 }
307
Joe Onorato9c1289c2009-08-17 11:03:03 -0400308 final String action = intent.getAction();
309 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
310
311 if (packageName == null || packageName.length() == 0) {
312 // they sent us a bad intent
313 return;
314 }
315
316 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
317 mAllAppsList.updatePackage(context, packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400318 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
319 if (!replacing) {
320 mAllAppsList.removePackage(packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400321 }
322 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
323 // later, we will update the package at this time
324 } else {
325 if (!replacing) {
326 mAllAppsList.addPackage(context, packageName);
327 } else {
328 mAllAppsList.updatePackage(context, packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400329 }
330 }
331
332 if (mAllAppsList.added.size() > 0) {
333 added = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800334 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400335 }
336 if (mAllAppsList.removed.size() > 0) {
337 removed = mAllAppsList.removed;
Romain Guy84f296c2009-11-04 15:00:44 -0800338 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400339 for (ApplicationInfo info: removed) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800340 mIconCache.remove(info.intent.getComponent());
Joe Onorato9c1289c2009-08-17 11:03:03 -0400341 }
342 }
343 if (mAllAppsList.modified.size() > 0) {
344 modified = mAllAppsList.modified;
Romain Guy84f296c2009-11-04 15:00:44 -0800345 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400346 }
347
Marco Nelissen3c8b90d2009-09-11 14:49:50 -0700348 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400349 if (callbacks == null) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800350 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400351 return;
352 }
353
354 if (added != null) {
355 final ArrayList<ApplicationInfo> addedFinal = added;
356 mHandler.post(new Runnable() {
357 public void run() {
358 callbacks.bindPackageAdded(addedFinal);
359 }
360 });
361 }
Joe Onorato418928e2009-11-19 18:05:36 -0800362 if (modified != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400363 final ArrayList<ApplicationInfo> modifiedFinal = modified;
364 mHandler.post(new Runnable() {
365 public void run() {
366 callbacks.bindPackageUpdated(packageName, modifiedFinal);
367 }
368 });
369 }
Joe Onorato418928e2009-11-19 18:05:36 -0800370 if (removed != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400371 final ArrayList<ApplicationInfo> removedFinal = removed;
372 mHandler.post(new Runnable() {
373 public void run() {
374 callbacks.bindPackageRemoved(packageName, removedFinal);
375 }
376 });
377 }
378 }
379 }
380
381 public class Loader {
382 private static final int ITEMS_CHUNK = 6;
383
384 private LoaderThread mLoaderThread;
385
386 private int mLastWorkspaceSeq = 0;
387 private int mWorkspaceSeq = 1;
388
389 private int mLastAllAppsSeq = 0;
390 private int mAllAppsSeq = 1;
391
Romain Guy84f296c2009-11-04 15:00:44 -0800392 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
393 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500394 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400395
396 /**
397 * Call this from the ui thread so the handler is initialized on the correct thread.
398 */
399 public Loader() {
400 }
401
402 public void startLoader(Context context, boolean isLaunching) {
403 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800404 if (DEBUG_LOADERS) {
405 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
406 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400407 // Don't bother to start the thread if we know it's not going to do anything
408 if (mCallbacks.get() != null) {
409 LoaderThread oldThread = mLoaderThread;
410 if (oldThread != null) {
411 if (oldThread.isLaunching()) {
412 // don't downgrade isLaunching if we're already running
413 isLaunching = true;
414 }
415 oldThread.stopLocked();
416 }
417 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
418 mLoaderThread.start();
419 }
420 }
421 }
422
423 public void stopLoader() {
424 synchronized (mLock) {
425 if (mLoaderThread != null) {
426 mLoaderThread.stopLocked();
427 }
428 }
429 }
430
431 public void setWorkspaceDirty() {
432 synchronized (mLock) {
433 mWorkspaceSeq++;
434 }
435 }
436
437 public void setAllAppsDirty() {
438 synchronized (mLock) {
439 mAllAppsSeq++;
440 }
441 }
442
443 /**
444 * Runnable for the thread that loads the contents of the launcher:
445 * - workspace icons
446 * - widgets
447 * - all apps icons
448 */
449 private class LoaderThread extends Thread {
450 private Context mContext;
451 private Thread mWaitThread;
452 private boolean mIsLaunching;
453 private boolean mStopped;
454 private boolean mWorkspaceDoneBinding;
455
456 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
457 mContext = context;
458 mWaitThread = waitThread;
459 mIsLaunching = isLaunching;
460 }
461
462 boolean isLaunching() {
463 return mIsLaunching;
464 }
465
466 /**
467 * If another LoaderThread was supplied, we need to wait for that to finish before
468 * we start our processing. This keeps the ordering of the setting and clearing
469 * of the dirty flags correct by making sure we don't start processing stuff until
470 * they've had a chance to re-set them. We do this waiting the worker thread, not
471 * the ui thread to avoid ANRs.
472 */
473 private void waitForOtherThread() {
474 if (mWaitThread != null) {
475 boolean done = false;
476 while (!done) {
477 try {
478 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700479 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400480 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800481 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400482 }
483 }
484 mWaitThread = null;
485 }
486 }
487
488 public void run() {
489 waitForOtherThread();
490
491 // Elevate priority when Home launches for the first time to avoid
492 // starving at boot time. Staring at a blank home is not cool.
493 synchronized (mLock) {
494 android.os.Process.setThreadPriority(mIsLaunching
495 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
496 }
497
498 // Load the workspace only if it's dirty.
499 int workspaceSeq;
500 boolean workspaceDirty;
501 synchronized (mLock) {
502 workspaceSeq = mWorkspaceSeq;
503 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
504 }
505 if (workspaceDirty) {
506 loadWorkspace();
507 }
508 synchronized (mLock) {
509 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
510 if (mStopped) {
511 return;
512 }
513 if (workspaceSeq == mWorkspaceSeq) {
514 mLastWorkspaceSeq = mWorkspaceSeq;
515 }
516 }
517
518 // Bind the workspace
519 bindWorkspace();
520
521 // Wait until the either we're stopped or the other threads are done.
522 // This way we don't start loading all apps until the workspace has settled
523 // down.
524 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500525 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400526 public void run() {
527 synchronized (LoaderThread.this) {
528 mWorkspaceDoneBinding = true;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800529 if (DEBUG_LOADERS) {
530 Log.d(TAG, "done with workspace");
531 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400532 LoaderThread.this.notify();
533 }
534 }
535 });
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800536 if (DEBUG_LOADERS) {
537 Log.d(TAG, "waiting to be done with workspace");
538 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400539 while (!mStopped && !mWorkspaceDoneBinding) {
540 try {
541 this.wait();
542 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800543 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400544 }
545 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800546 if (DEBUG_LOADERS) {
547 Log.d(TAG, "done waiting to be done with workspace");
548 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400549 }
550
551 // Load all apps if they're dirty
552 int allAppsSeq;
553 boolean allAppsDirty;
554 synchronized (mLock) {
555 allAppsSeq = mAllAppsSeq;
556 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800557 if (DEBUG_LOADERS) {
558 Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
559 + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
560 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400561 }
562 if (allAppsDirty) {
563 loadAllApps();
564 }
565 synchronized (mLock) {
566 // If we're not stopped, and nobody has incremented mAllAppsSeq.
567 if (mStopped) {
568 return;
569 }
570 if (allAppsSeq == mAllAppsSeq) {
571 mLastAllAppsSeq = mAllAppsSeq;
572 }
573 }
574
575 // Bind all apps
Joe Onorato34b02492009-10-14 11:13:48 -0700576 if (allAppsDirty) {
577 bindAllApps();
578 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400579
580 // Clear out this reference, otherwise we end up holding it until all of the
581 // callback runnables are done.
582 mContext = null;
583
584 synchronized (mLock) {
585 // Setting the reference is atomic, but we can't do it inside the other critical
586 // sections.
587 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400588 }
589 }
590
591 public void stopLocked() {
592 synchronized (LoaderThread.this) {
593 mStopped = true;
594 this.notify();
595 }
596 }
597
598 /**
599 * Gets the callbacks object. If we've been stopped, or if the launcher object
600 * has somehow been garbage collected, return null instead.
601 */
602 Callbacks tryGetCallbacks() {
603 synchronized (mLock) {
604 if (mStopped) {
605 return null;
606 }
607
608 final Callbacks callbacks = mCallbacks.get();
609 if (callbacks == null) {
610 Log.w(TAG, "no mCallbacks");
611 return null;
612 }
613
614 return callbacks;
615 }
616 }
617
618 private void loadWorkspace() {
619 long t = SystemClock.uptimeMillis();
620
621 final Context context = mContext;
622 final ContentResolver contentResolver = context.getContentResolver();
623 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800624 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800625 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400626
627 /* TODO
628 if (mLocaleChanged) {
629 updateShortcutLabels(contentResolver, manager);
630 }
631 */
632
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400633 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500634 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800635 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400636
Romain Guy5c16f3e2010-01-12 17:24:58 -0800637 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
638
Joe Onorato9c1289c2009-08-17 11:03:03 -0400639 final Cursor c = contentResolver.query(
640 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
641
642 try {
643 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
644 final int intentIndex = c.getColumnIndexOrThrow
645 (LauncherSettings.Favorites.INTENT);
646 final int titleIndex = c.getColumnIndexOrThrow
647 (LauncherSettings.Favorites.TITLE);
648 final int iconTypeIndex = c.getColumnIndexOrThrow(
649 LauncherSettings.Favorites.ICON_TYPE);
650 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
651 final int iconPackageIndex = c.getColumnIndexOrThrow(
652 LauncherSettings.Favorites.ICON_PACKAGE);
653 final int iconResourceIndex = c.getColumnIndexOrThrow(
654 LauncherSettings.Favorites.ICON_RESOURCE);
655 final int containerIndex = c.getColumnIndexOrThrow(
656 LauncherSettings.Favorites.CONTAINER);
657 final int itemTypeIndex = c.getColumnIndexOrThrow(
658 LauncherSettings.Favorites.ITEM_TYPE);
659 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
660 LauncherSettings.Favorites.APPWIDGET_ID);
661 final int screenIndex = c.getColumnIndexOrThrow(
662 LauncherSettings.Favorites.SCREEN);
663 final int cellXIndex = c.getColumnIndexOrThrow
664 (LauncherSettings.Favorites.CELLX);
665 final int cellYIndex = c.getColumnIndexOrThrow
666 (LauncherSettings.Favorites.CELLY);
667 final int spanXIndex = c.getColumnIndexOrThrow
668 (LauncherSettings.Favorites.SPANX);
669 final int spanYIndex = c.getColumnIndexOrThrow(
670 LauncherSettings.Favorites.SPANY);
671 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
672 final int displayModeIndex = c.getColumnIndexOrThrow(
673 LauncherSettings.Favorites.DISPLAY_MODE);
674
Joe Onorato0589f0f2010-02-08 13:44:00 -0800675 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400676 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400677 LauncherAppWidgetInfo appWidgetInfo;
678 int container;
679 long id;
680 Intent intent;
681
682 while (!mStopped && c.moveToNext()) {
683 try {
684 int itemType = c.getInt(itemTypeIndex);
685
686 switch (itemType) {
687 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
688 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
689 intentDescription = c.getString(intentIndex);
690 try {
691 intent = Intent.parseUri(intentDescription, 0);
692 } catch (URISyntaxException e) {
693 continue;
694 }
695
696 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800697 info = getShortcutInfo(manager, intent, context);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400698 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800699 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato9c1289c2009-08-17 11:03:03 -0400700 iconPackageIndex, iconResourceIndex, iconIndex);
701 }
702
703 if (info == null) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800704 info = new ShortcutInfo();
705 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -0400706 }
707
708 if (info != null) {
Joe Onorato028b6242009-11-10 18:26:13 -0800709 if (itemType
710 != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
711 info.title = c.getString(titleIndex);
712 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400713 info.intent = intent;
714
715 info.id = c.getLong(idIndex);
716 container = c.getInt(containerIndex);
717 info.container = container;
718 info.screen = c.getInt(screenIndex);
719 info.cellX = c.getInt(cellXIndex);
720 info.cellY = c.getInt(cellYIndex);
721
722 switch (container) {
723 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
724 mItems.add(info);
725 break;
726 default:
727 // Item is in a user folder
728 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500729 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400730 folderInfo.add(info);
731 break;
732 }
733 }
734 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400735
Joe Onoratoad72e172009-11-06 16:25:04 -0500736 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400737 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500738 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400739
740 folderInfo.title = c.getString(titleIndex);
741
742 folderInfo.id = id;
743 container = c.getInt(containerIndex);
744 folderInfo.container = container;
745 folderInfo.screen = c.getInt(screenIndex);
746 folderInfo.cellX = c.getInt(cellXIndex);
747 folderInfo.cellY = c.getInt(cellYIndex);
748
749 switch (container) {
750 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
751 mItems.add(folderInfo);
752 break;
753 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500754
755 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400756 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500757
Joe Onorato9c1289c2009-08-17 11:03:03 -0400758 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400759 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800760 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400761
Romain Guy5c16f3e2010-01-12 17:24:58 -0800762 // Make sure the live folder exists
763 final ProviderInfo providerInfo =
764 context.getPackageManager().resolveContentProvider(
765 uri.getAuthority(), 0);
766
767 if (providerInfo == null && !isSafeMode) {
768 itemsToRemove.add(id);
769 } else {
770 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
771
772 intentDescription = c.getString(intentIndex);
773 intent = null;
774 if (intentDescription != null) {
775 try {
776 intent = Intent.parseUri(intentDescription, 0);
777 } catch (URISyntaxException e) {
778 // Ignore, a live folder might not have a base intent
779 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400780 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800781
782 liveFolderInfo.title = c.getString(titleIndex);
783 liveFolderInfo.id = id;
784 liveFolderInfo.uri = uri;
785 container = c.getInt(containerIndex);
786 liveFolderInfo.container = container;
787 liveFolderInfo.screen = c.getInt(screenIndex);
788 liveFolderInfo.cellX = c.getInt(cellXIndex);
789 liveFolderInfo.cellY = c.getInt(cellYIndex);
790 liveFolderInfo.baseIntent = intent;
791 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
792
793 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
794 iconResourceIndex, liveFolderInfo);
795
796 switch (container) {
797 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
798 mItems.add(liveFolderInfo);
799 break;
800 }
801 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400802 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400803 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500804
Joe Onorato9c1289c2009-08-17 11:03:03 -0400805 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
806 // Read all Launcher-specific widget details
807 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800808 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400809
Romain Guy629de3e2010-01-13 12:20:59 -0800810 final AppWidgetProviderInfo provider =
811 widgets.getAppWidgetInfo(appWidgetId);
812
813 if (!isSafeMode && (provider == null || provider.provider == null ||
814 provider.provider.getPackageName() == null)) {
815 itemsToRemove.add(id);
816 } else {
817 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
818 appWidgetInfo.id = id;
819 appWidgetInfo.screen = c.getInt(screenIndex);
820 appWidgetInfo.cellX = c.getInt(cellXIndex);
821 appWidgetInfo.cellY = c.getInt(cellYIndex);
822 appWidgetInfo.spanX = c.getInt(spanXIndex);
823 appWidgetInfo.spanY = c.getInt(spanYIndex);
824
825 container = c.getInt(containerIndex);
826 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
827 Log.e(TAG, "Widget found where container "
828 + "!= CONTAINER_DESKTOP -- ignoring!");
829 continue;
830 }
831 appWidgetInfo.container = c.getInt(containerIndex);
832
833 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400834 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400835 break;
836 }
837 } catch (Exception e) {
838 Log.w(TAG, "Desktop items loading interrupted:", e);
839 }
840 }
841 } finally {
842 c.close();
843 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800844
845 if (itemsToRemove.size() > 0) {
846 ContentProviderClient client = contentResolver.acquireContentProviderClient(
847 LauncherSettings.Favorites.CONTENT_URI);
848 // Remove dead items
849 for (long id : itemsToRemove) {
850 if (DEBUG_LOADERS) {
851 Log.d(TAG, "Removed id = " + id);
852 }
853 // Don't notify content observers
854 try {
855 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
856 null, null);
857 } catch (RemoteException e) {
858 Log.w(TAG, "Could not remove id = " + id);
859 }
860 }
861 }
862
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800863 if (DEBUG_LOADERS) {
864 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
865 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400866 }
867
868 /**
869 * Read everything out of our database.
870 */
871 private void bindWorkspace() {
872 final long t = SystemClock.uptimeMillis();
873
874 // Don't use these two variables in any of the callback runnables.
875 // Otherwise we hold a reference to them.
876 Callbacks callbacks = mCallbacks.get();
877 if (callbacks == null) {
878 // This launcher has exited and nobody bothered to tell us. Just bail.
879 Log.w(TAG, "LoaderThread running with no launcher");
880 return;
881 }
882
883 int N;
884 // Tell the workspace that we're about to start firing items at it
885 mHandler.post(new Runnable() {
886 public void run() {
887 Callbacks callbacks = tryGetCallbacks();
888 if (callbacks != null) {
889 callbacks.startBinding();
890 }
891 }
892 });
893 // Add the items to the workspace.
894 N = mItems.size();
895 for (int i=0; i<N; i+=ITEMS_CHUNK) {
896 final int start = i;
897 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
898 mHandler.post(new Runnable() {
899 public void run() {
900 Callbacks callbacks = tryGetCallbacks();
901 if (callbacks != null) {
902 callbacks.bindItems(mItems, start, start+chunkSize);
903 }
904 }
905 });
906 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500907 mHandler.post(new Runnable() {
908 public void run() {
909 Callbacks callbacks = tryGetCallbacks();
910 if (callbacks != null) {
911 callbacks.bindFolders(mFolders);
912 }
913 }
914 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400915 // Wait until the queue goes empty.
916 mHandler.postIdle(new Runnable() {
917 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800918 if (DEBUG_LOADERS) {
919 Log.d(TAG, "Going to start binding widgets soon.");
920 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400921 }
922 });
923 // Bind the widgets, one at a time.
924 // WARNING: this is calling into the workspace from the background thread,
925 // but since getCurrentScreen() just returns the int, we should be okay. This
926 // is just a hint for the order, and if it's wrong, we'll be okay.
927 // TODO: instead, we should have that push the current screen into here.
928 final int currentScreen = callbacks.getCurrentWorkspaceScreen();
929 N = mAppWidgets.size();
930 // once for the current screen
931 for (int i=0; i<N; i++) {
932 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
933 if (widget.screen == currentScreen) {
934 mHandler.post(new Runnable() {
935 public void run() {
936 Callbacks callbacks = tryGetCallbacks();
937 if (callbacks != null) {
938 callbacks.bindAppWidget(widget);
939 }
940 }
941 });
942 }
943 }
944 // once for the other screens
945 for (int i=0; i<N; i++) {
946 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
947 if (widget.screen != currentScreen) {
948 mHandler.post(new Runnable() {
949 public void run() {
950 Callbacks callbacks = tryGetCallbacks();
951 if (callbacks != null) {
952 callbacks.bindAppWidget(widget);
953 }
954 }
955 });
956 }
957 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400958 // Tell the workspace that we're done.
959 mHandler.post(new Runnable() {
960 public void run() {
961 Callbacks callbacks = tryGetCallbacks();
962 if (callbacks != null) {
963 callbacks.finishBindingItems();
964 }
965 }
966 });
967 // If we're profiling, this is the last thing in the queue.
968 mHandler.post(new Runnable() {
969 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800970 if (DEBUG_LOADERS) {
971 Log.d(TAG, "bound workspace in "
972 + (SystemClock.uptimeMillis()-t) + "ms");
973 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400974 if (Launcher.PROFILE_ROTATE) {
975 android.os.Debug.stopMethodTracing();
976 }
977 }
978 });
979 }
980
981 private void loadAllApps() {
982 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
983 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
984
985 final Callbacks callbacks = tryGetCallbacks();
986 if (callbacks == null) {
987 return;
988 }
989
Joe Onorato0589f0f2010-02-08 13:44:00 -0800990 final PackageManager packageManager = mContext.getPackageManager();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400991 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
992
993 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400994 mBeforeFirstLoad = false;
995
Joe Onorato9c1289c2009-08-17 11:03:03 -0400996 mAllAppsList.clear();
997 if (apps != null) {
998 long t = SystemClock.uptimeMillis();
999
1000 int N = apps.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001001 for (int i=0; i<N && !mStopped; i++) {
1002 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001003 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001004 }
Joe Onoratob0c27f22009-12-01 16:19:38 -08001005 Collections.sort(mAllAppsList.data, APP_NAME_COMPARATOR);
1006 Collections.sort(mAllAppsList.added, APP_NAME_COMPARATOR);
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001007 if (DEBUG_LOADERS) {
1008 Log.d(TAG, "cached app icons in "
1009 + (SystemClock.uptimeMillis()-t) + "ms");
1010 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001011 }
1012 }
1013 }
1014
1015 private void bindAllApps() {
1016 synchronized (mLock) {
Joe Onorato0c4513e2009-11-19 12:24:48 -08001017 final ArrayList<ApplicationInfo> results
Romain Guy5c16f3e2010-01-12 17:24:58 -08001018 = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
Joe Onorato0c4513e2009-11-19 12:24:48 -08001019 // We're adding this now, so clear out this so we don't re-send them.
Romain Guy84f296c2009-11-04 15:00:44 -08001020 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001021 mHandler.post(new Runnable() {
1022 public void run() {
Joe Onorato34b02492009-10-14 11:13:48 -07001023 final long t = SystemClock.uptimeMillis();
1024 final int count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001025
1026 Callbacks callbacks = tryGetCallbacks();
1027 if (callbacks != null) {
1028 callbacks.bindAllApplications(results);
1029 }
1030
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001031 if (DEBUG_LOADERS) {
1032 Log.d(TAG, "bound app " + count + " icons in "
Romain Guy5c16f3e2010-01-12 17:24:58 -08001033 + (SystemClock.uptimeMillis() - t) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001034 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001035 }
1036 });
1037 }
1038 }
Joe Onoratobe386092009-11-17 17:32:16 -08001039
1040 public void dumpState() {
1041 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1042 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1043 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1044 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1045 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1046 }
1047 }
1048
1049 public void dumpState() {
1050 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1051 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1052 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1053 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1054 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1055 if (mLoaderThread != null) {
1056 mLoaderThread.dumpState();
1057 } else {
1058 Log.d(TAG, "mLoader.mLoaderThread=null");
1059 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001060 }
1061 }
1062
1063 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001064 * Make an ShortcutInfo object for a sortcut that is an application.
Joe Onorato9c1289c2009-08-17 11:03:03 -04001065 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001066 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001067 Context context) {
1068 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1069
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001070 if (resolveInfo == null) {
1071 return null;
1072 }
1073
Joe Onorato0589f0f2010-02-08 13:44:00 -08001074 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001075 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001076 info.setIcon(mIconCache.getIcon(intent.getComponent(), resolveInfo));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001077 if (info.title == null || info.title.length() == 0) {
1078 info.title = activityInfo.loadLabel(manager);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001079 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001080 if (info.title == null) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001081 info.title = activityInfo.name;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001082 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001083 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1084 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001085 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001086
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001087 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001088 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001089 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001090 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001091 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001092
Joe Onorato0589f0f2010-02-08 13:44:00 -08001093 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001094 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001095
Joe Onorato9c1289c2009-08-17 11:03:03 -04001096 int iconType = c.getInt(iconTypeIndex);
1097 switch (iconType) {
1098 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1099 String packageName = c.getString(iconPackageIndex);
1100 String resourceName = c.getString(iconResourceIndex);
1101 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001103 Resources resources = packageManager.getResourcesForApplication(packageName);
1104 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001105 info.setIcon(Utilities.createIconBitmap(resources.getDrawable(id), context));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001106 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001107 info.setIcon(getDefaultIcon());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001108 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001109 info.iconResource = new Intent.ShortcutIconResource();
1110 info.iconResource.packageName = packageName;
1111 info.iconResource.resourceName = resourceName;
1112 info.customIcon = false;
1113 break;
1114 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1115 byte[] data = c.getBlob(iconIndex);
1116 try {
1117 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001118 info.setIcon(bitmap);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001119 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001120 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -04001121 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001122 info.customIcon = true;
1123 break;
1124 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001125 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -04001126 info.customIcon = false;
1127 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001128 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001129 return info;
1130 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001131
Joe Onorato0589f0f2010-02-08 13:44:00 -08001132 ShortcutInfo addShortcut(Context context, Intent data,
1133 CellLayout.CellInfo cellInfo, boolean notify) {
1134
1135 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1136 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1137 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1138
1139 return info;
1140 }
1141
1142 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1143 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1144 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1145 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1146
1147 Bitmap icon = null;
1148 boolean filtered = false;
1149 boolean customIcon = false;
1150 ShortcutIconResource iconResource = null;
1151
1152 if (bitmap != null && bitmap instanceof Bitmap) {
1153 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1154 filtered = true;
1155 customIcon = true;
1156 } else {
1157 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1158 if (extra != null && extra instanceof ShortcutIconResource) {
1159 try {
1160 iconResource = (ShortcutIconResource) extra;
1161 final PackageManager packageManager = context.getPackageManager();
1162 Resources resources = packageManager.getResourcesForApplication(
1163 iconResource.packageName);
1164 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1165 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1166 } catch (Exception e) {
1167 Log.w(TAG, "Could not load shortcut icon: " + extra);
1168 }
1169 }
1170 }
1171
1172 if (icon == null) {
1173 icon = getDefaultIcon();
1174 }
1175
1176 final ShortcutInfo info = new ShortcutInfo();
1177 info.setIcon(icon);
1178 info.title = name;
1179 info.intent = intent;
1180 info.customIcon = customIcon;
1181 info.iconResource = iconResource;
1182
1183 return info;
1184 }
1185
Joe Onorato9c1289c2009-08-17 11:03:03 -04001186 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1187 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1188
1189 int iconType = c.getInt(iconTypeIndex);
1190 switch (iconType) {
1191 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1192 String packageName = c.getString(iconPackageIndex);
1193 String resourceName = c.getString(iconResourceIndex);
1194 PackageManager packageManager = context.getPackageManager();
1195 try {
1196 Resources resources = packageManager.getResourcesForApplication(packageName);
1197 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001198 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1199 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001200 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001201 liveFolderInfo.icon = Utilities.createIconBitmap(
1202 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1203 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001204 }
1205 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1206 liveFolderInfo.iconResource.packageName = packageName;
1207 liveFolderInfo.iconResource.resourceName = resourceName;
1208 break;
1209 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001210 liveFolderInfo.icon = Utilities.createIconBitmap(
1211 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1212 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001213 }
1214 }
1215
1216 /**
1217 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1218 * or make a new one.
1219 */
1220 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1221 // See if a placeholder was created for us already
1222 FolderInfo folderInfo = folders.get(id);
1223 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1224 // No placeholder -- create a new instance
1225 folderInfo = new UserFolderInfo();
1226 folders.put(id, folderInfo);
1227 }
1228 return (UserFolderInfo) folderInfo;
1229 }
1230
1231 /**
1232 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1233 * new one.
1234 */
1235 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1236 // See if a placeholder was created for us already
1237 FolderInfo folderInfo = folders.get(id);
1238 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1239 // No placeholder -- create a new instance
1240 folderInfo = new LiveFolderInfo();
1241 folders.put(id, folderInfo);
1242 }
1243 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001244 }
1245
1246 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
1247 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -07001248 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001249 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
1250 null, null, null);
1251
Romain Guy73b979d2009-06-09 12:57:21 -07001252 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001253 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1254 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1255 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1256
1257 // boolean changed = false;
1258
1259 try {
1260 while (c.moveToNext()) {
1261 try {
1262 if (c.getInt(itemTypeIndex) !=
1263 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1264 continue;
1265 }
1266
1267 final String intentUri = c.getString(intentIndex);
1268 if (intentUri != null) {
Romain Guy1ce1a242009-06-23 17:34:54 -07001269 final Intent shortcut = Intent.parseUri(intentUri, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001270 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
1271 final ComponentName name = shortcut.getComponent();
1272 if (name != null) {
1273 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
1274 final String title = c.getString(titleIndex);
1275 String label = getLabel(manager, activityInfo);
1276
1277 if (title == null || !title.equals(label)) {
1278 final ContentValues values = new ContentValues();
1279 values.put(LauncherSettings.Favorites.TITLE, label);
1280
Romain Guyfedc4fc2009-03-27 20:48:20 -07001281 resolver.update(
1282 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001283 values, "_id=?",
1284 new String[] { String.valueOf(c.getLong(idIndex)) });
1285
1286 // changed = true;
1287 }
1288 }
1289 }
1290 }
1291 } catch (URISyntaxException e) {
1292 // Ignore
1293 } catch (PackageManager.NameNotFoundException e) {
1294 // Ignore
1295 }
1296 }
1297 } finally {
1298 c.close();
1299 }
1300
1301 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
1302 }
1303
1304 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1305 String label = activityInfo.loadLabel(manager).toString();
1306 if (label == null) {
1307 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1308 if (label == null) {
1309 label = activityInfo.name;
1310 }
1311 }
1312 return label;
1313 }
1314
Joe Onorato9c1289c2009-08-17 11:03:03 -04001315 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001316 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001317 = new Comparator<ApplicationInfo>() {
1318 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1319 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001320 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001321 };
Joe Onoratobe386092009-11-17 17:32:16 -08001322
1323 public void dumpState() {
1324 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1325 Log.d(TAG, "mCallbacks=" + mCallbacks);
1326 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1327 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1328 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1329 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1330 mLoader.dumpState();
1331 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001332}