blob: 8790fd7bd8aaf7356e17dc6fdaee726c6e95bb62 [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
Romain Guyedcce092010-03-04 13:03:17 -080053import com.android.launcher.R;
54
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055/**
56 * Maintains in-memory state of the Launcher. It is expected that there should be only one
57 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070058 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040060public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080061 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040062 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070063
Joe Onoratof99f8c12009-10-31 17:27:36 -040064 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040065 private final Object mLock = new Object();
66 private DeferredHandler mHandler = new DeferredHandler();
67 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068
Joe Onoratof99f8c12009-10-31 17:27:36 -040069 private boolean mBeforeFirstLoad = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -040070 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080071
Joe Onorato0589f0f2010-02-08 13:44:00 -080072 private AllAppsList mAllAppsList;
73 private IconCache mIconCache;
74
75 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080076
Joe Onorato9c1289c2009-08-17 11:03:03 -040077 public interface Callbacks {
78 public int getCurrentWorkspaceScreen();
79 public void startBinding();
80 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050081 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040082 public void finishBindingItems();
83 public void bindAppWidget(LauncherAppWidgetInfo info);
84 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
85 public void bindPackageAdded(ArrayList<ApplicationInfo> apps);
86 public void bindPackageUpdated(String packageName, ArrayList<ApplicationInfo> apps);
87 public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps);
88 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089
Joe Onorato0589f0f2010-02-08 13:44:00 -080090 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -040091 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -080092 mAllAppsList = new AllAppsList(iconCache);
93 mIconCache = iconCache;
94
95 mDefaultIcon = Utilities.createIconBitmap(
96 app.getPackageManager().getDefaultActivityIcon(), app);
97 }
98
99 public Bitmap getDefaultIcon() {
100 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400101 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102
Joe Onorato9c1289c2009-08-17 11:03:03 -0400103 /**
104 * Adds an item to the DB if it was not created previously, or move it to a new
105 * <container, screen, cellX, cellY>
106 */
107 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
108 int screen, int cellX, int cellY) {
109 if (item.container == ItemInfo.NO_ID) {
110 // From all apps
111 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
112 } else {
113 // From somewhere else
114 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800115 }
116 }
117
118 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400119 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700120 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400121 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
122 int cellX, int cellY) {
123 item.container = container;
124 item.screen = screen;
125 item.cellX = cellX;
126 item.cellY = cellY;
127
128 final ContentValues values = new ContentValues();
129 final ContentResolver cr = context.getContentResolver();
130
131 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
132 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
133 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
134 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
135
136 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700137 }
138
139 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400140 * Returns true if the shortcuts already exists in the database.
141 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800142 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400143 static boolean shortcutExists(Context context, String title, Intent intent) {
144 final ContentResolver cr = context.getContentResolver();
145 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
146 new String[] { "title", "intent" }, "title=? and intent=?",
147 new String[] { title, intent.toUri(0) }, null);
148 boolean result = false;
149 try {
150 result = c.moveToFirst();
151 } finally {
152 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400154 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700155 }
156
Joe Onorato9c1289c2009-08-17 11:03:03 -0400157 /**
158 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
159 */
160 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
161 final ContentResolver cr = context.getContentResolver();
162 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
163 "_id=? and (itemType=? or itemType=?)",
164 new String[] { String.valueOf(id),
165 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
166 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700167
Joe Onorato9c1289c2009-08-17 11:03:03 -0400168 try {
169 if (c.moveToFirst()) {
170 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
171 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
172 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
173 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
174 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
175 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800176
Joe Onorato9c1289c2009-08-17 11:03:03 -0400177 FolderInfo folderInfo = null;
178 switch (c.getInt(itemTypeIndex)) {
179 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
180 folderInfo = findOrMakeUserFolder(folderList, id);
181 break;
182 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
183 folderInfo = findOrMakeLiveFolder(folderList, id);
184 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700185 }
186
Joe Onorato9c1289c2009-08-17 11:03:03 -0400187 folderInfo.title = c.getString(titleIndex);
188 folderInfo.id = id;
189 folderInfo.container = c.getInt(containerIndex);
190 folderInfo.screen = c.getInt(screenIndex);
191 folderInfo.cellX = c.getInt(cellXIndex);
192 folderInfo.cellY = c.getInt(cellYIndex);
193
194 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700195 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400196 } finally {
197 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700198 }
199
200 return null;
201 }
202
Joe Onorato9c1289c2009-08-17 11:03:03 -0400203 /**
204 * Add an item to the database in a specified container. Sets the container, screen, cellX and
205 * cellY fields of the item. Also assigns an ID to the item.
206 */
207 static void addItemToDatabase(Context context, ItemInfo item, long container,
208 int screen, int cellX, int cellY, boolean notify) {
209 item.container = container;
210 item.screen = screen;
211 item.cellX = cellX;
212 item.cellY = cellY;
213
214 final ContentValues values = new ContentValues();
215 final ContentResolver cr = context.getContentResolver();
216
217 item.onAddToDatabase(values);
218
219 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
220 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
221
222 if (result != null) {
223 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700224 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700225 }
226
Joe Onorato9c1289c2009-08-17 11:03:03 -0400227 /**
228 * Update an item to the database in a specified container.
229 */
230 static void updateItemInDatabase(Context context, ItemInfo item) {
231 final ContentValues values = new ContentValues();
232 final ContentResolver cr = context.getContentResolver();
233
234 item.onAddToDatabase(values);
235
236 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
237 }
238
239 /**
240 * Removes the specified item from the database
241 * @param context
242 * @param item
243 */
244 static void deleteItemFromDatabase(Context context, ItemInfo item) {
245 final ContentResolver cr = context.getContentResolver();
246
247 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
248 }
249
250 /**
251 * Remove the contents of the specified folder from the database
252 */
253 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
254 final ContentResolver cr = context.getContentResolver();
255
256 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
257 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
258 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
259 }
260
261 /**
262 * Set this as the current Launcher activity object for the loader.
263 */
264 public void initialize(Callbacks callbacks) {
265 synchronized (mLock) {
266 mCallbacks = new WeakReference<Callbacks>(callbacks);
267 }
268 }
269
270 public void startLoader(Context context, boolean isLaunching) {
271 mLoader.startLoader(context, isLaunching);
272 }
273
274 public void stopLoader() {
275 mLoader.stopLoader();
276 }
277
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700278 /**
279 * We pick up most of the changes to all apps.
280 */
281 public void setAllAppsDirty() {
282 mLoader.setAllAppsDirty();
283 }
284
Joe Onorato9c1289c2009-08-17 11:03:03 -0400285 public void setWorkspaceDirty() {
286 mLoader.setWorkspaceDirty();
287 }
288
289 /**
290 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
291 * ACTION_PACKAGE_CHANGED.
292 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400293 public void onReceive(Context context, Intent intent) {
294 // Use the app as the context.
295 context = mApp;
296
Joe Onorato9c1289c2009-08-17 11:03:03 -0400297 final String packageName = intent.getData().getSchemeSpecificPart();
298
299 ArrayList<ApplicationInfo> added = null;
300 ArrayList<ApplicationInfo> removed = null;
301 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400302
303 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400304 if (mBeforeFirstLoad) {
305 // If we haven't even loaded yet, don't bother, since we'll just pick
306 // up the changes.
307 return;
308 }
309
Joe Onorato9c1289c2009-08-17 11:03:03 -0400310 final String action = intent.getAction();
311 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
312
313 if (packageName == null || packageName.length() == 0) {
314 // they sent us a bad intent
315 return;
316 }
317
318 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
319 mAllAppsList.updatePackage(context, packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400320 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
321 if (!replacing) {
322 mAllAppsList.removePackage(packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400323 }
324 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
325 // later, we will update the package at this time
326 } else {
327 if (!replacing) {
328 mAllAppsList.addPackage(context, packageName);
329 } else {
330 mAllAppsList.updatePackage(context, packageName);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400331 }
332 }
333
334 if (mAllAppsList.added.size() > 0) {
335 added = mAllAppsList.added;
Romain Guy84f296c2009-11-04 15:00:44 -0800336 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400337 }
338 if (mAllAppsList.removed.size() > 0) {
339 removed = mAllAppsList.removed;
Romain Guy84f296c2009-11-04 15:00:44 -0800340 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400341 for (ApplicationInfo info: removed) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800342 mIconCache.remove(info.intent.getComponent());
Joe Onorato9c1289c2009-08-17 11:03:03 -0400343 }
344 }
345 if (mAllAppsList.modified.size() > 0) {
346 modified = mAllAppsList.modified;
Romain Guy84f296c2009-11-04 15:00:44 -0800347 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400348 }
349
Marco Nelissen3c8b90d2009-09-11 14:49:50 -0700350 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400351 if (callbacks == null) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800352 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400353 return;
354 }
355
356 if (added != null) {
357 final ArrayList<ApplicationInfo> addedFinal = added;
358 mHandler.post(new Runnable() {
359 public void run() {
360 callbacks.bindPackageAdded(addedFinal);
361 }
362 });
363 }
Joe Onorato418928e2009-11-19 18:05:36 -0800364 if (modified != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400365 final ArrayList<ApplicationInfo> modifiedFinal = modified;
366 mHandler.post(new Runnable() {
367 public void run() {
368 callbacks.bindPackageUpdated(packageName, modifiedFinal);
369 }
370 });
371 }
Joe Onorato418928e2009-11-19 18:05:36 -0800372 if (removed != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400373 final ArrayList<ApplicationInfo> removedFinal = removed;
374 mHandler.post(new Runnable() {
375 public void run() {
376 callbacks.bindPackageRemoved(packageName, removedFinal);
377 }
378 });
379 }
380 }
381 }
382
383 public class Loader {
384 private static final int ITEMS_CHUNK = 6;
385
386 private LoaderThread mLoaderThread;
387
388 private int mLastWorkspaceSeq = 0;
389 private int mWorkspaceSeq = 1;
390
391 private int mLastAllAppsSeq = 0;
392 private int mAllAppsSeq = 1;
393
Romain Guy84f296c2009-11-04 15:00:44 -0800394 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
395 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500396 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400397
398 /**
399 * Call this from the ui thread so the handler is initialized on the correct thread.
400 */
401 public Loader() {
402 }
403
404 public void startLoader(Context context, boolean isLaunching) {
405 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800406 if (DEBUG_LOADERS) {
407 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
408 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400409 // Don't bother to start the thread if we know it's not going to do anything
410 if (mCallbacks.get() != null) {
411 LoaderThread oldThread = mLoaderThread;
412 if (oldThread != null) {
413 if (oldThread.isLaunching()) {
414 // don't downgrade isLaunching if we're already running
415 isLaunching = true;
416 }
417 oldThread.stopLocked();
418 }
419 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
420 mLoaderThread.start();
421 }
422 }
423 }
424
425 public void stopLoader() {
426 synchronized (mLock) {
427 if (mLoaderThread != null) {
428 mLoaderThread.stopLocked();
429 }
430 }
431 }
432
433 public void setWorkspaceDirty() {
434 synchronized (mLock) {
435 mWorkspaceSeq++;
436 }
437 }
438
439 public void setAllAppsDirty() {
440 synchronized (mLock) {
441 mAllAppsSeq++;
442 }
443 }
444
445 /**
446 * Runnable for the thread that loads the contents of the launcher:
447 * - workspace icons
448 * - widgets
449 * - all apps icons
450 */
451 private class LoaderThread extends Thread {
452 private Context mContext;
453 private Thread mWaitThread;
454 private boolean mIsLaunching;
455 private boolean mStopped;
456 private boolean mWorkspaceDoneBinding;
457
458 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
459 mContext = context;
460 mWaitThread = waitThread;
461 mIsLaunching = isLaunching;
462 }
463
464 boolean isLaunching() {
465 return mIsLaunching;
466 }
467
468 /**
469 * If another LoaderThread was supplied, we need to wait for that to finish before
470 * we start our processing. This keeps the ordering of the setting and clearing
471 * of the dirty flags correct by making sure we don't start processing stuff until
472 * they've had a chance to re-set them. We do this waiting the worker thread, not
473 * the ui thread to avoid ANRs.
474 */
475 private void waitForOtherThread() {
476 if (mWaitThread != null) {
477 boolean done = false;
478 while (!done) {
479 try {
480 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700481 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400482 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800483 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400484 }
485 }
486 mWaitThread = null;
487 }
488 }
489
490 public void run() {
491 waitForOtherThread();
492
493 // Elevate priority when Home launches for the first time to avoid
494 // starving at boot time. Staring at a blank home is not cool.
495 synchronized (mLock) {
496 android.os.Process.setThreadPriority(mIsLaunching
497 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
498 }
499
500 // Load the workspace only if it's dirty.
501 int workspaceSeq;
502 boolean workspaceDirty;
503 synchronized (mLock) {
504 workspaceSeq = mWorkspaceSeq;
505 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
506 }
507 if (workspaceDirty) {
508 loadWorkspace();
509 }
510 synchronized (mLock) {
511 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
512 if (mStopped) {
513 return;
514 }
515 if (workspaceSeq == mWorkspaceSeq) {
516 mLastWorkspaceSeq = mWorkspaceSeq;
517 }
518 }
519
520 // Bind the workspace
521 bindWorkspace();
522
523 // Wait until the either we're stopped or the other threads are done.
524 // This way we don't start loading all apps until the workspace has settled
525 // down.
526 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500527 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400528 public void run() {
529 synchronized (LoaderThread.this) {
530 mWorkspaceDoneBinding = true;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800531 if (DEBUG_LOADERS) {
532 Log.d(TAG, "done with workspace");
533 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400534 LoaderThread.this.notify();
535 }
536 }
537 });
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800538 if (DEBUG_LOADERS) {
539 Log.d(TAG, "waiting to be done with workspace");
540 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400541 while (!mStopped && !mWorkspaceDoneBinding) {
542 try {
543 this.wait();
544 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800545 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400546 }
547 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800548 if (DEBUG_LOADERS) {
549 Log.d(TAG, "done waiting to be done with workspace");
550 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400551 }
552
553 // Load all apps if they're dirty
554 int allAppsSeq;
555 boolean allAppsDirty;
556 synchronized (mLock) {
557 allAppsSeq = mAllAppsSeq;
558 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800559 if (DEBUG_LOADERS) {
560 Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
561 + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
562 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400563 }
564 if (allAppsDirty) {
565 loadAllApps();
566 }
567 synchronized (mLock) {
568 // If we're not stopped, and nobody has incremented mAllAppsSeq.
569 if (mStopped) {
570 return;
571 }
572 if (allAppsSeq == mAllAppsSeq) {
573 mLastAllAppsSeq = mAllAppsSeq;
574 }
575 }
576
577 // Bind all apps
Joe Onorato34b02492009-10-14 11:13:48 -0700578 if (allAppsDirty) {
579 bindAllApps();
580 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400581
582 // Clear out this reference, otherwise we end up holding it until all of the
583 // callback runnables are done.
584 mContext = null;
585
586 synchronized (mLock) {
587 // Setting the reference is atomic, but we can't do it inside the other critical
588 // sections.
589 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400590 }
591 }
592
593 public void stopLocked() {
594 synchronized (LoaderThread.this) {
595 mStopped = true;
596 this.notify();
597 }
598 }
599
600 /**
601 * Gets the callbacks object. If we've been stopped, or if the launcher object
602 * has somehow been garbage collected, return null instead.
603 */
604 Callbacks tryGetCallbacks() {
605 synchronized (mLock) {
606 if (mStopped) {
607 return null;
608 }
609
610 final Callbacks callbacks = mCallbacks.get();
611 if (callbacks == null) {
612 Log.w(TAG, "no mCallbacks");
613 return null;
614 }
615
616 return callbacks;
617 }
618 }
619
620 private void loadWorkspace() {
621 long t = SystemClock.uptimeMillis();
622
623 final Context context = mContext;
624 final ContentResolver contentResolver = context.getContentResolver();
625 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800626 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800627 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400628
629 /* TODO
630 if (mLocaleChanged) {
631 updateShortcutLabels(contentResolver, manager);
632 }
633 */
634
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400635 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500636 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800637 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400638
Romain Guy5c16f3e2010-01-12 17:24:58 -0800639 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
640
Joe Onorato9c1289c2009-08-17 11:03:03 -0400641 final Cursor c = contentResolver.query(
642 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
643
644 try {
645 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
646 final int intentIndex = c.getColumnIndexOrThrow
647 (LauncherSettings.Favorites.INTENT);
648 final int titleIndex = c.getColumnIndexOrThrow
649 (LauncherSettings.Favorites.TITLE);
650 final int iconTypeIndex = c.getColumnIndexOrThrow(
651 LauncherSettings.Favorites.ICON_TYPE);
652 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
653 final int iconPackageIndex = c.getColumnIndexOrThrow(
654 LauncherSettings.Favorites.ICON_PACKAGE);
655 final int iconResourceIndex = c.getColumnIndexOrThrow(
656 LauncherSettings.Favorites.ICON_RESOURCE);
657 final int containerIndex = c.getColumnIndexOrThrow(
658 LauncherSettings.Favorites.CONTAINER);
659 final int itemTypeIndex = c.getColumnIndexOrThrow(
660 LauncherSettings.Favorites.ITEM_TYPE);
661 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
662 LauncherSettings.Favorites.APPWIDGET_ID);
663 final int screenIndex = c.getColumnIndexOrThrow(
664 LauncherSettings.Favorites.SCREEN);
665 final int cellXIndex = c.getColumnIndexOrThrow
666 (LauncherSettings.Favorites.CELLX);
667 final int cellYIndex = c.getColumnIndexOrThrow
668 (LauncherSettings.Favorites.CELLY);
669 final int spanXIndex = c.getColumnIndexOrThrow
670 (LauncherSettings.Favorites.SPANX);
671 final int spanYIndex = c.getColumnIndexOrThrow(
672 LauncherSettings.Favorites.SPANY);
673 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
674 final int displayModeIndex = c.getColumnIndexOrThrow(
675 LauncherSettings.Favorites.DISPLAY_MODE);
676
Joe Onorato0589f0f2010-02-08 13:44:00 -0800677 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400678 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400679 LauncherAppWidgetInfo appWidgetInfo;
680 int container;
681 long id;
682 Intent intent;
683
684 while (!mStopped && c.moveToNext()) {
685 try {
686 int itemType = c.getInt(itemTypeIndex);
687
688 switch (itemType) {
689 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
690 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
691 intentDescription = c.getString(intentIndex);
692 try {
693 intent = Intent.parseUri(intentDescription, 0);
694 } catch (URISyntaxException e) {
695 continue;
696 }
697
698 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800699 info = getShortcutInfo(manager, intent, context);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400700 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800701 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato9c1289c2009-08-17 11:03:03 -0400702 iconPackageIndex, iconResourceIndex, iconIndex);
703 }
704
705 if (info == null) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800706 info = new ShortcutInfo();
707 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -0400708 }
709
710 if (info != null) {
Joe Onorato028b6242009-11-10 18:26:13 -0800711 if (itemType
712 != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
713 info.title = c.getString(titleIndex);
714 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400715 info.intent = intent;
716
717 info.id = c.getLong(idIndex);
718 container = c.getInt(containerIndex);
719 info.container = container;
720 info.screen = c.getInt(screenIndex);
721 info.cellX = c.getInt(cellXIndex);
722 info.cellY = c.getInt(cellYIndex);
723
724 switch (container) {
725 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
726 mItems.add(info);
727 break;
728 default:
729 // Item is in a user folder
730 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500731 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400732 folderInfo.add(info);
733 break;
734 }
735 }
736 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400737
Joe Onoratoad72e172009-11-06 16:25:04 -0500738 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400739 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500740 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400741
742 folderInfo.title = c.getString(titleIndex);
743
744 folderInfo.id = id;
745 container = c.getInt(containerIndex);
746 folderInfo.container = container;
747 folderInfo.screen = c.getInt(screenIndex);
748 folderInfo.cellX = c.getInt(cellXIndex);
749 folderInfo.cellY = c.getInt(cellYIndex);
750
751 switch (container) {
752 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
753 mItems.add(folderInfo);
754 break;
755 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500756
757 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400758 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500759
Joe Onorato9c1289c2009-08-17 11:03:03 -0400760 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400761 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800762 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400763
Romain Guy5c16f3e2010-01-12 17:24:58 -0800764 // Make sure the live folder exists
765 final ProviderInfo providerInfo =
766 context.getPackageManager().resolveContentProvider(
767 uri.getAuthority(), 0);
768
769 if (providerInfo == null && !isSafeMode) {
770 itemsToRemove.add(id);
771 } else {
772 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
773
774 intentDescription = c.getString(intentIndex);
775 intent = null;
776 if (intentDescription != null) {
777 try {
778 intent = Intent.parseUri(intentDescription, 0);
779 } catch (URISyntaxException e) {
780 // Ignore, a live folder might not have a base intent
781 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400782 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800783
784 liveFolderInfo.title = c.getString(titleIndex);
785 liveFolderInfo.id = id;
786 liveFolderInfo.uri = uri;
787 container = c.getInt(containerIndex);
788 liveFolderInfo.container = container;
789 liveFolderInfo.screen = c.getInt(screenIndex);
790 liveFolderInfo.cellX = c.getInt(cellXIndex);
791 liveFolderInfo.cellY = c.getInt(cellYIndex);
792 liveFolderInfo.baseIntent = intent;
793 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
794
795 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
796 iconResourceIndex, liveFolderInfo);
797
798 switch (container) {
799 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
800 mItems.add(liveFolderInfo);
801 break;
802 }
803 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400804 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400805 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500806
Joe Onorato9c1289c2009-08-17 11:03:03 -0400807 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
808 // Read all Launcher-specific widget details
809 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800810 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400811
Romain Guy629de3e2010-01-13 12:20:59 -0800812 final AppWidgetProviderInfo provider =
813 widgets.getAppWidgetInfo(appWidgetId);
814
815 if (!isSafeMode && (provider == null || provider.provider == null ||
816 provider.provider.getPackageName() == null)) {
817 itemsToRemove.add(id);
818 } else {
819 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
820 appWidgetInfo.id = id;
821 appWidgetInfo.screen = c.getInt(screenIndex);
822 appWidgetInfo.cellX = c.getInt(cellXIndex);
823 appWidgetInfo.cellY = c.getInt(cellYIndex);
824 appWidgetInfo.spanX = c.getInt(spanXIndex);
825 appWidgetInfo.spanY = c.getInt(spanYIndex);
826
827 container = c.getInt(containerIndex);
828 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
829 Log.e(TAG, "Widget found where container "
830 + "!= CONTAINER_DESKTOP -- ignoring!");
831 continue;
832 }
833 appWidgetInfo.container = c.getInt(containerIndex);
834
835 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400836 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400837 break;
838 }
839 } catch (Exception e) {
840 Log.w(TAG, "Desktop items loading interrupted:", e);
841 }
842 }
843 } finally {
844 c.close();
845 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800846
847 if (itemsToRemove.size() > 0) {
848 ContentProviderClient client = contentResolver.acquireContentProviderClient(
849 LauncherSettings.Favorites.CONTENT_URI);
850 // Remove dead items
851 for (long id : itemsToRemove) {
852 if (DEBUG_LOADERS) {
853 Log.d(TAG, "Removed id = " + id);
854 }
855 // Don't notify content observers
856 try {
857 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
858 null, null);
859 } catch (RemoteException e) {
860 Log.w(TAG, "Could not remove id = " + id);
861 }
862 }
863 }
864
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800865 if (DEBUG_LOADERS) {
866 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
867 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400868 }
869
870 /**
871 * Read everything out of our database.
872 */
873 private void bindWorkspace() {
874 final long t = SystemClock.uptimeMillis();
875
876 // Don't use these two variables in any of the callback runnables.
877 // Otherwise we hold a reference to them.
878 Callbacks callbacks = mCallbacks.get();
879 if (callbacks == null) {
880 // This launcher has exited and nobody bothered to tell us. Just bail.
881 Log.w(TAG, "LoaderThread running with no launcher");
882 return;
883 }
884
885 int N;
886 // Tell the workspace that we're about to start firing items at it
887 mHandler.post(new Runnable() {
888 public void run() {
889 Callbacks callbacks = tryGetCallbacks();
890 if (callbacks != null) {
891 callbacks.startBinding();
892 }
893 }
894 });
895 // Add the items to the workspace.
896 N = mItems.size();
897 for (int i=0; i<N; i+=ITEMS_CHUNK) {
898 final int start = i;
899 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
900 mHandler.post(new Runnable() {
901 public void run() {
902 Callbacks callbacks = tryGetCallbacks();
903 if (callbacks != null) {
904 callbacks.bindItems(mItems, start, start+chunkSize);
905 }
906 }
907 });
908 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500909 mHandler.post(new Runnable() {
910 public void run() {
911 Callbacks callbacks = tryGetCallbacks();
912 if (callbacks != null) {
913 callbacks.bindFolders(mFolders);
914 }
915 }
916 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400917 // Wait until the queue goes empty.
918 mHandler.postIdle(new Runnable() {
919 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800920 if (DEBUG_LOADERS) {
921 Log.d(TAG, "Going to start binding widgets soon.");
922 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400923 }
924 });
925 // Bind the widgets, one at a time.
926 // WARNING: this is calling into the workspace from the background thread,
927 // but since getCurrentScreen() just returns the int, we should be okay. This
928 // is just a hint for the order, and if it's wrong, we'll be okay.
929 // TODO: instead, we should have that push the current screen into here.
930 final int currentScreen = callbacks.getCurrentWorkspaceScreen();
931 N = mAppWidgets.size();
932 // once for the current screen
933 for (int i=0; i<N; i++) {
934 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
935 if (widget.screen == currentScreen) {
936 mHandler.post(new Runnable() {
937 public void run() {
938 Callbacks callbacks = tryGetCallbacks();
939 if (callbacks != null) {
940 callbacks.bindAppWidget(widget);
941 }
942 }
943 });
944 }
945 }
946 // once for the other screens
947 for (int i=0; i<N; i++) {
948 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
949 if (widget.screen != currentScreen) {
950 mHandler.post(new Runnable() {
951 public void run() {
952 Callbacks callbacks = tryGetCallbacks();
953 if (callbacks != null) {
954 callbacks.bindAppWidget(widget);
955 }
956 }
957 });
958 }
959 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400960 // Tell the workspace that we're done.
961 mHandler.post(new Runnable() {
962 public void run() {
963 Callbacks callbacks = tryGetCallbacks();
964 if (callbacks != null) {
965 callbacks.finishBindingItems();
966 }
967 }
968 });
969 // If we're profiling, this is the last thing in the queue.
970 mHandler.post(new Runnable() {
971 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800972 if (DEBUG_LOADERS) {
973 Log.d(TAG, "bound workspace in "
974 + (SystemClock.uptimeMillis()-t) + "ms");
975 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400976 if (Launcher.PROFILE_ROTATE) {
977 android.os.Debug.stopMethodTracing();
978 }
979 }
980 });
981 }
982
983 private void loadAllApps() {
984 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
985 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
986
987 final Callbacks callbacks = tryGetCallbacks();
988 if (callbacks == null) {
989 return;
990 }
991
Joe Onorato0589f0f2010-02-08 13:44:00 -0800992 final PackageManager packageManager = mContext.getPackageManager();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400993 final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
994
995 synchronized (mLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400996 mBeforeFirstLoad = false;
997
Joe Onorato9c1289c2009-08-17 11:03:03 -0400998 mAllAppsList.clear();
999 if (apps != null) {
1000 long t = SystemClock.uptimeMillis();
1001
1002 int N = apps.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001003 for (int i=0; i<N && !mStopped; i++) {
1004 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001005 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001006 }
Joe Onoratob0c27f22009-12-01 16:19:38 -08001007 Collections.sort(mAllAppsList.data, APP_NAME_COMPARATOR);
1008 Collections.sort(mAllAppsList.added, APP_NAME_COMPARATOR);
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001009 if (DEBUG_LOADERS) {
1010 Log.d(TAG, "cached app icons in "
1011 + (SystemClock.uptimeMillis()-t) + "ms");
1012 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001013 }
1014 }
1015 }
1016
1017 private void bindAllApps() {
1018 synchronized (mLock) {
Joe Onorato0c4513e2009-11-19 12:24:48 -08001019 final ArrayList<ApplicationInfo> results
Romain Guy5c16f3e2010-01-12 17:24:58 -08001020 = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
Joe Onorato0c4513e2009-11-19 12:24:48 -08001021 // We're adding this now, so clear out this so we don't re-send them.
Romain Guy84f296c2009-11-04 15:00:44 -08001022 mAllAppsList.added = new ArrayList<ApplicationInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001023 mHandler.post(new Runnable() {
1024 public void run() {
Joe Onorato34b02492009-10-14 11:13:48 -07001025 final long t = SystemClock.uptimeMillis();
1026 final int count = results.size();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001027
1028 Callbacks callbacks = tryGetCallbacks();
1029 if (callbacks != null) {
1030 callbacks.bindAllApplications(results);
1031 }
1032
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001033 if (DEBUG_LOADERS) {
1034 Log.d(TAG, "bound app " + count + " icons in "
Romain Guy5c16f3e2010-01-12 17:24:58 -08001035 + (SystemClock.uptimeMillis() - t) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001036 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001037 }
1038 });
1039 }
1040 }
Joe Onoratobe386092009-11-17 17:32:16 -08001041
1042 public void dumpState() {
1043 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1044 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1045 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1046 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1047 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1048 }
1049 }
1050
1051 public void dumpState() {
1052 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1053 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1054 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1055 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1056 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1057 if (mLoaderThread != null) {
1058 mLoaderThread.dumpState();
1059 } else {
1060 Log.d(TAG, "mLoader.mLoaderThread=null");
1061 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001062 }
1063 }
1064
1065 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001066 * Make an ShortcutInfo object for a sortcut that is an application.
Joe Onorato9c1289c2009-08-17 11:03:03 -04001067 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001068 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001069 Context context) {
1070 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1071
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001072 if (resolveInfo == null) {
1073 return null;
1074 }
1075
Joe Onorato0589f0f2010-02-08 13:44:00 -08001076 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001077 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001078 info.setIcon(mIconCache.getIcon(intent.getComponent(), resolveInfo));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001079 if (info.title == null || info.title.length() == 0) {
1080 info.title = activityInfo.loadLabel(manager);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001081 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001082 if (info.title == null) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001083 info.title = activityInfo.name;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001084 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001085 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1086 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001087 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001088
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001089 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001090 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001091 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001092 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001093 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001094
Joe Onorato0589f0f2010-02-08 13:44:00 -08001095 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001096 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001097
Joe Onorato9c1289c2009-08-17 11:03:03 -04001098 int iconType = c.getInt(iconTypeIndex);
1099 switch (iconType) {
1100 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1101 String packageName = c.getString(iconPackageIndex);
1102 String resourceName = c.getString(iconResourceIndex);
1103 PackageManager packageManager = context.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001104 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001105 Resources resources = packageManager.getResourcesForApplication(packageName);
1106 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001107 info.setIcon(Utilities.createIconBitmap(resources.getDrawable(id), context));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001108 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001109 info.setIcon(getDefaultIcon());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001110 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001111 info.iconResource = new Intent.ShortcutIconResource();
1112 info.iconResource.packageName = packageName;
1113 info.iconResource.resourceName = resourceName;
1114 info.customIcon = false;
1115 break;
1116 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
1117 byte[] data = c.getBlob(iconIndex);
1118 try {
1119 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001120 info.setIcon(bitmap);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001121 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001122 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -04001123 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001124 info.customIcon = true;
1125 break;
1126 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001127 info.setIcon(getDefaultIcon());
Joe Onorato9c1289c2009-08-17 11:03:03 -04001128 info.customIcon = false;
1129 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001130 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001131 return info;
1132 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001133
Joe Onorato0589f0f2010-02-08 13:44:00 -08001134 ShortcutInfo addShortcut(Context context, Intent data,
1135 CellLayout.CellInfo cellInfo, boolean notify) {
1136
1137 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1138 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1139 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1140
1141 return info;
1142 }
1143
1144 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1145 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1146 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1147 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1148
1149 Bitmap icon = null;
1150 boolean filtered = false;
1151 boolean customIcon = false;
1152 ShortcutIconResource iconResource = null;
1153
1154 if (bitmap != null && bitmap instanceof Bitmap) {
1155 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1156 filtered = true;
1157 customIcon = true;
1158 } else {
1159 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1160 if (extra != null && extra instanceof ShortcutIconResource) {
1161 try {
1162 iconResource = (ShortcutIconResource) extra;
1163 final PackageManager packageManager = context.getPackageManager();
1164 Resources resources = packageManager.getResourcesForApplication(
1165 iconResource.packageName);
1166 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1167 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1168 } catch (Exception e) {
1169 Log.w(TAG, "Could not load shortcut icon: " + extra);
1170 }
1171 }
1172 }
1173
1174 if (icon == null) {
1175 icon = getDefaultIcon();
1176 }
1177
1178 final ShortcutInfo info = new ShortcutInfo();
1179 info.setIcon(icon);
1180 info.title = name;
1181 info.intent = intent;
1182 info.customIcon = customIcon;
1183 info.iconResource = iconResource;
1184
1185 return info;
1186 }
1187
Joe Onorato9c1289c2009-08-17 11:03:03 -04001188 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1189 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1190
1191 int iconType = c.getInt(iconTypeIndex);
1192 switch (iconType) {
1193 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1194 String packageName = c.getString(iconPackageIndex);
1195 String resourceName = c.getString(iconResourceIndex);
1196 PackageManager packageManager = context.getPackageManager();
1197 try {
1198 Resources resources = packageManager.getResourcesForApplication(packageName);
1199 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001200 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1201 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001202 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001203 liveFolderInfo.icon = Utilities.createIconBitmap(
1204 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1205 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001206 }
1207 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1208 liveFolderInfo.iconResource.packageName = packageName;
1209 liveFolderInfo.iconResource.resourceName = resourceName;
1210 break;
1211 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001212 liveFolderInfo.icon = Utilities.createIconBitmap(
1213 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1214 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001215 }
1216 }
1217
1218 /**
1219 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1220 * or make a new one.
1221 */
1222 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1223 // See if a placeholder was created for us already
1224 FolderInfo folderInfo = folders.get(id);
1225 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1226 // No placeholder -- create a new instance
1227 folderInfo = new UserFolderInfo();
1228 folders.put(id, folderInfo);
1229 }
1230 return (UserFolderInfo) folderInfo;
1231 }
1232
1233 /**
1234 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1235 * new one.
1236 */
1237 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1238 // See if a placeholder was created for us already
1239 FolderInfo folderInfo = folders.get(id);
1240 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1241 // No placeholder -- create a new instance
1242 folderInfo = new LiveFolderInfo();
1243 folders.put(id, folderInfo);
1244 }
1245 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
1247
1248 private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
1249 final Cursor c = resolver.query(LauncherSettings.Favorites.CONTENT_URI,
Romain Guy73b979d2009-06-09 12:57:21 -07001250 new String[] { LauncherSettings.Favorites._ID, LauncherSettings.Favorites.TITLE,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001251 LauncherSettings.Favorites.INTENT, LauncherSettings.Favorites.ITEM_TYPE },
1252 null, null, null);
1253
Romain Guy73b979d2009-06-09 12:57:21 -07001254 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001255 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1256 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1257 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1258
1259 // boolean changed = false;
1260
1261 try {
1262 while (c.moveToNext()) {
1263 try {
1264 if (c.getInt(itemTypeIndex) !=
1265 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1266 continue;
1267 }
1268
1269 final String intentUri = c.getString(intentIndex);
1270 if (intentUri != null) {
Romain Guy1ce1a242009-06-23 17:34:54 -07001271 final Intent shortcut = Intent.parseUri(intentUri, 0);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001272 if (Intent.ACTION_MAIN.equals(shortcut.getAction())) {
1273 final ComponentName name = shortcut.getComponent();
1274 if (name != null) {
1275 final ActivityInfo activityInfo = manager.getActivityInfo(name, 0);
1276 final String title = c.getString(titleIndex);
1277 String label = getLabel(manager, activityInfo);
1278
1279 if (title == null || !title.equals(label)) {
1280 final ContentValues values = new ContentValues();
1281 values.put(LauncherSettings.Favorites.TITLE, label);
1282
Romain Guyfedc4fc2009-03-27 20:48:20 -07001283 resolver.update(
1284 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001285 values, "_id=?",
1286 new String[] { String.valueOf(c.getLong(idIndex)) });
1287
1288 // changed = true;
1289 }
1290 }
1291 }
1292 }
1293 } catch (URISyntaxException e) {
1294 // Ignore
1295 } catch (PackageManager.NameNotFoundException e) {
1296 // Ignore
1297 }
1298 }
1299 } finally {
1300 c.close();
1301 }
1302
1303 // if (changed) resolver.notifyChange(Settings.Favorites.CONTENT_URI, null);
1304 }
1305
1306 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1307 String label = activityInfo.loadLabel(manager).toString();
1308 if (label == null) {
1309 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1310 if (label == null) {
1311 label = activityInfo.name;
1312 }
1313 }
1314 return label;
1315 }
1316
Joe Onorato9c1289c2009-08-17 11:03:03 -04001317 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001318 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001319 = new Comparator<ApplicationInfo>() {
1320 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1321 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001322 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001323 };
Joe Onoratobe386092009-11-17 17:32:16 -08001324
1325 public void dumpState() {
1326 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1327 Log.d(TAG, "mCallbacks=" + mCallbacks);
1328 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1329 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1330 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1331 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1332 mLoader.dumpState();
1333 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001334}