blob: a8672008c589cd8dc766c0030c409d8fdc2c4f81 [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 Onorato56d82912010-03-07 14:32:10 -050048import java.util.Arrays;
Joe Onorato9c1289c2009-08-17 11:03:03 -040049import java.util.Comparator;
50import java.util.Collections;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import java.util.HashMap;
52import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053
Romain Guyedcce092010-03-04 13:03:17 -080054import com.android.launcher.R;
55
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056/**
57 * Maintains in-memory state of the Launcher. It is expected that there should be only one
58 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070059 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080060 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040061public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080062 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040063 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070064
Joe Onoratod65d08e2010-04-20 15:43:37 -040065 private int mBatchSize; // 0 is all apps at once
Daniel Sandler2ff10b32010-04-16 15:06:06 -040066 private int mAllAppsLoadDelay; // milliseconds between batches
Daniel Sandlerdca66122010-04-13 16:23:58 -040067
Joe Onoratof99f8c12009-10-31 17:27:36 -040068 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040069 private final Object mLock = new Object();
70 private DeferredHandler mHandler = new DeferredHandler();
71 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072
Joe Onoratod65d08e2010-04-20 15:43:37 -040073 private boolean mBeforeFirstLoad = true; // only access this from main thread
Joe Onorato9c1289c2009-08-17 11:03:03 -040074 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075
Joe Onoratofad1fb52010-05-04 12:12:41 -070076 private final Object mAllAppsListLock = new Object();
Joe Onorato0589f0f2010-02-08 13:44:00 -080077 private AllAppsList mAllAppsList;
78 private IconCache mIconCache;
79
80 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081
Joe Onorato9c1289c2009-08-17 11:03:03 -040082 public interface Callbacks {
83 public int getCurrentWorkspaceScreen();
84 public void startBinding();
85 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050086 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040087 public void finishBindingItems();
88 public void bindAppWidget(LauncherAppWidgetInfo info);
89 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -050090 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
91 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
92 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
Joe Onorato9c1289c2009-08-17 11:03:03 -040093 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094
Joe Onorato0589f0f2010-02-08 13:44:00 -080095 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -040096 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -080097 mAllAppsList = new AllAppsList(iconCache);
98 mIconCache = iconCache;
99
100 mDefaultIcon = Utilities.createIconBitmap(
101 app.getPackageManager().getDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400102
103 mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
Joe Onoratod65d08e2010-04-20 15:43:37 -0400104
105 mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800106 }
107
Joe Onorato56d82912010-03-07 14:32:10 -0500108 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800109 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400110 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111
Joe Onorato9c1289c2009-08-17 11:03:03 -0400112 /**
113 * Adds an item to the DB if it was not created previously, or move it to a new
114 * <container, screen, cellX, cellY>
115 */
116 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
117 int screen, int cellX, int cellY) {
118 if (item.container == ItemInfo.NO_ID) {
119 // From all apps
120 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
121 } else {
122 // From somewhere else
123 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 }
125 }
126
127 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400128 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700129 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400130 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
131 int cellX, int cellY) {
132 item.container = container;
133 item.screen = screen;
134 item.cellX = cellX;
135 item.cellY = cellY;
136
137 final ContentValues values = new ContentValues();
138 final ContentResolver cr = context.getContentResolver();
139
140 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
141 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
142 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
143 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
144
145 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700146 }
147
148 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400149 * Returns true if the shortcuts already exists in the database.
150 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800151 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400152 static boolean shortcutExists(Context context, String title, Intent intent) {
153 final ContentResolver cr = context.getContentResolver();
154 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
155 new String[] { "title", "intent" }, "title=? and intent=?",
156 new String[] { title, intent.toUri(0) }, null);
157 boolean result = false;
158 try {
159 result = c.moveToFirst();
160 } finally {
161 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800162 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400163 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700164 }
165
Joe Onorato9c1289c2009-08-17 11:03:03 -0400166 /**
167 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
168 */
169 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
170 final ContentResolver cr = context.getContentResolver();
171 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
172 "_id=? and (itemType=? or itemType=?)",
173 new String[] { String.valueOf(id),
174 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
175 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700176
Joe Onorato9c1289c2009-08-17 11:03:03 -0400177 try {
178 if (c.moveToFirst()) {
179 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
180 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
181 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
182 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
183 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
184 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800185
Joe Onorato9c1289c2009-08-17 11:03:03 -0400186 FolderInfo folderInfo = null;
187 switch (c.getInt(itemTypeIndex)) {
188 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
189 folderInfo = findOrMakeUserFolder(folderList, id);
190 break;
191 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
192 folderInfo = findOrMakeLiveFolder(folderList, id);
193 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700194 }
195
Joe Onorato9c1289c2009-08-17 11:03:03 -0400196 folderInfo.title = c.getString(titleIndex);
197 folderInfo.id = id;
198 folderInfo.container = c.getInt(containerIndex);
199 folderInfo.screen = c.getInt(screenIndex);
200 folderInfo.cellX = c.getInt(cellXIndex);
201 folderInfo.cellY = c.getInt(cellYIndex);
202
203 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700204 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400205 } finally {
206 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700207 }
208
209 return null;
210 }
211
Joe Onorato9c1289c2009-08-17 11:03:03 -0400212 /**
213 * Add an item to the database in a specified container. Sets the container, screen, cellX and
214 * cellY fields of the item. Also assigns an ID to the item.
215 */
216 static void addItemToDatabase(Context context, ItemInfo item, long container,
217 int screen, int cellX, int cellY, boolean notify) {
218 item.container = container;
219 item.screen = screen;
220 item.cellX = cellX;
221 item.cellY = cellY;
222
223 final ContentValues values = new ContentValues();
224 final ContentResolver cr = context.getContentResolver();
225
226 item.onAddToDatabase(values);
227
228 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
229 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
230
231 if (result != null) {
232 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700233 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700234 }
235
Joe Onorato9c1289c2009-08-17 11:03:03 -0400236 /**
237 * Update an item to the database in a specified container.
238 */
239 static void updateItemInDatabase(Context context, ItemInfo item) {
240 final ContentValues values = new ContentValues();
241 final ContentResolver cr = context.getContentResolver();
242
243 item.onAddToDatabase(values);
244
245 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
246 }
247
248 /**
249 * Removes the specified item from the database
250 * @param context
251 * @param item
252 */
253 static void deleteItemFromDatabase(Context context, ItemInfo item) {
254 final ContentResolver cr = context.getContentResolver();
255
256 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
257 }
258
259 /**
260 * Remove the contents of the specified folder from the database
261 */
262 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
263 final ContentResolver cr = context.getContentResolver();
264
265 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
266 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
267 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
268 }
269
270 /**
271 * Set this as the current Launcher activity object for the loader.
272 */
273 public void initialize(Callbacks callbacks) {
274 synchronized (mLock) {
275 mCallbacks = new WeakReference<Callbacks>(callbacks);
276 }
277 }
278
279 public void startLoader(Context context, boolean isLaunching) {
280 mLoader.startLoader(context, isLaunching);
281 }
282
283 public void stopLoader() {
284 mLoader.stopLoader();
285 }
286
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700287 /**
288 * We pick up most of the changes to all apps.
289 */
290 public void setAllAppsDirty() {
291 mLoader.setAllAppsDirty();
292 }
293
Joe Onorato9c1289c2009-08-17 11:03:03 -0400294 public void setWorkspaceDirty() {
295 mLoader.setWorkspaceDirty();
296 }
297
298 /**
299 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
300 * ACTION_PACKAGE_CHANGED.
301 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400302 public void onReceive(Context context, Intent intent) {
303 // Use the app as the context.
304 context = mApp;
305
Joe Onorato9c1289c2009-08-17 11:03:03 -0400306 ArrayList<ApplicationInfo> added = null;
307 ArrayList<ApplicationInfo> removed = null;
308 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400309
Joe Onoratofad1fb52010-05-04 12:12:41 -0700310 synchronized (mAllAppsListLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400311 if (mBeforeFirstLoad) {
312 // If we haven't even loaded yet, don't bother, since we'll just pick
313 // up the changes.
314 return;
315 }
316
Joe Onorato9c1289c2009-08-17 11:03:03 -0400317 final String action = intent.getAction();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400318
Joe Onorato64e6be72010-03-05 15:05:52 -0500319 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
320 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
321 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
322 final String packageName = intent.getData().getSchemeSpecificPart();
323 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400324
Joe Onorato64e6be72010-03-05 15:05:52 -0500325 if (packageName == null || packageName.length() == 0) {
326 // they sent us a bad intent
327 return;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400328 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500329
330 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400331 mAllAppsList.updatePackage(context, packageName);
Joe Onorato64e6be72010-03-05 15:05:52 -0500332 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
333 if (!replacing) {
334 mAllAppsList.removePackage(packageName);
335 }
336 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
337 // later, we will update the package at this time
338 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
339 if (!replacing) {
340 mAllAppsList.addPackage(context, packageName);
341 } else {
342 mAllAppsList.updatePackage(context, packageName);
343 }
344 }
Joe Onorato56d82912010-03-07 14:32:10 -0500345
346 if (mAllAppsList.added.size() > 0) {
347 added = mAllAppsList.added;
348 mAllAppsList.added = new ArrayList<ApplicationInfo>();
349 }
350 if (mAllAppsList.removed.size() > 0) {
351 removed = mAllAppsList.removed;
352 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
353 for (ApplicationInfo info: removed) {
354 mIconCache.remove(info.intent.getComponent());
355 }
356 }
357 if (mAllAppsList.modified.size() > 0) {
358 modified = mAllAppsList.modified;
359 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
360 }
361
362 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
363 if (callbacks == null) {
364 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
365 return;
366 }
367
368 if (added != null) {
369 final ArrayList<ApplicationInfo> addedFinal = added;
370 mHandler.post(new Runnable() {
371 public void run() {
372 callbacks.bindAppsAdded(addedFinal);
373 }
374 });
375 }
376 if (modified != null) {
377 final ArrayList<ApplicationInfo> modifiedFinal = modified;
378 mHandler.post(new Runnable() {
379 public void run() {
380 callbacks.bindAppsUpdated(modifiedFinal);
381 }
382 });
383 }
384 if (removed != null) {
385 final ArrayList<ApplicationInfo> removedFinal = removed;
386 mHandler.post(new Runnable() {
387 public void run() {
388 callbacks.bindAppsRemoved(removedFinal);
389 }
390 });
391 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500392 } else {
393 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
394 String packages[] = intent.getStringArrayExtra(
395 Intent.EXTRA_CHANGED_PACKAGE_LIST);
396 if (packages == null || packages.length == 0) {
397 return;
398 }
Joe Onorato56d82912010-03-07 14:32:10 -0500399 setAllAppsDirty();
400 setWorkspaceDirty();
401 startLoader(context, false);
Joe Onorato64e6be72010-03-05 15:05:52 -0500402 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
403 String packages[] = intent.getStringArrayExtra(
404 Intent.EXTRA_CHANGED_PACKAGE_LIST);
405 if (packages == null || packages.length == 0) {
406 return;
407 }
Joe Onorato56d82912010-03-07 14:32:10 -0500408 setAllAppsDirty();
409 setWorkspaceDirty();
410 startLoader(context, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400411 }
412 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400413 }
414 }
415
416 public class Loader {
417 private static final int ITEMS_CHUNK = 6;
418
419 private LoaderThread mLoaderThread;
420
421 private int mLastWorkspaceSeq = 0;
422 private int mWorkspaceSeq = 1;
423
424 private int mLastAllAppsSeq = 0;
425 private int mAllAppsSeq = 1;
426
Romain Guy84f296c2009-11-04 15:00:44 -0800427 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
428 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500429 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400430
431 /**
432 * Call this from the ui thread so the handler is initialized on the correct thread.
433 */
434 public Loader() {
435 }
436
437 public void startLoader(Context context, boolean isLaunching) {
438 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800439 if (DEBUG_LOADERS) {
440 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
441 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400442 // Don't bother to start the thread if we know it's not going to do anything
Joe Onoratoac033302010-04-13 17:19:18 -0700443 if (mCallbacks != null && mCallbacks.get() != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400444 LoaderThread oldThread = mLoaderThread;
445 if (oldThread != null) {
446 if (oldThread.isLaunching()) {
447 // don't downgrade isLaunching if we're already running
448 isLaunching = true;
449 }
450 oldThread.stopLocked();
451 }
452 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
453 mLoaderThread.start();
454 }
455 }
456 }
457
458 public void stopLoader() {
459 synchronized (mLock) {
460 if (mLoaderThread != null) {
461 mLoaderThread.stopLocked();
462 }
463 }
464 }
465
466 public void setWorkspaceDirty() {
467 synchronized (mLock) {
468 mWorkspaceSeq++;
469 }
470 }
471
472 public void setAllAppsDirty() {
473 synchronized (mLock) {
474 mAllAppsSeq++;
475 }
476 }
477
478 /**
479 * Runnable for the thread that loads the contents of the launcher:
480 * - workspace icons
481 * - widgets
482 * - all apps icons
483 */
484 private class LoaderThread extends Thread {
485 private Context mContext;
486 private Thread mWaitThread;
487 private boolean mIsLaunching;
488 private boolean mStopped;
489 private boolean mWorkspaceDoneBinding;
490
491 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
492 mContext = context;
493 mWaitThread = waitThread;
494 mIsLaunching = isLaunching;
495 }
496
497 boolean isLaunching() {
498 return mIsLaunching;
499 }
500
501 /**
502 * If another LoaderThread was supplied, we need to wait for that to finish before
503 * we start our processing. This keeps the ordering of the setting and clearing
504 * of the dirty flags correct by making sure we don't start processing stuff until
505 * they've had a chance to re-set them. We do this waiting the worker thread, not
506 * the ui thread to avoid ANRs.
507 */
508 private void waitForOtherThread() {
509 if (mWaitThread != null) {
510 boolean done = false;
511 while (!done) {
512 try {
513 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700514 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400515 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800516 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400517 }
518 }
519 mWaitThread = null;
520 }
521 }
522
523 public void run() {
524 waitForOtherThread();
525
526 // Elevate priority when Home launches for the first time to avoid
527 // starving at boot time. Staring at a blank home is not cool.
528 synchronized (mLock) {
529 android.os.Process.setThreadPriority(mIsLaunching
530 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
531 }
532
533 // Load the workspace only if it's dirty.
534 int workspaceSeq;
535 boolean workspaceDirty;
536 synchronized (mLock) {
537 workspaceSeq = mWorkspaceSeq;
538 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
539 }
540 if (workspaceDirty) {
541 loadWorkspace();
542 }
543 synchronized (mLock) {
544 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
545 if (mStopped) {
546 return;
547 }
548 if (workspaceSeq == mWorkspaceSeq) {
549 mLastWorkspaceSeq = mWorkspaceSeq;
550 }
551 }
552
553 // Bind the workspace
554 bindWorkspace();
555
556 // Wait until the either we're stopped or the other threads are done.
557 // This way we don't start loading all apps until the workspace has settled
558 // down.
559 synchronized (LoaderThread.this) {
Joe Onorato080d9b62009-11-02 12:01:11 -0500560 mHandler.postIdle(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400561 public void run() {
562 synchronized (LoaderThread.this) {
563 mWorkspaceDoneBinding = true;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800564 if (DEBUG_LOADERS) {
565 Log.d(TAG, "done with workspace");
566 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400567 LoaderThread.this.notify();
568 }
569 }
570 });
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800571 if (DEBUG_LOADERS) {
572 Log.d(TAG, "waiting to be done with workspace");
573 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400574 while (!mStopped && !mWorkspaceDoneBinding) {
575 try {
576 this.wait();
577 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800578 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400579 }
580 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800581 if (DEBUG_LOADERS) {
582 Log.d(TAG, "done waiting to be done with workspace");
583 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400584 }
585
Daniel Sandlerdca66122010-04-13 16:23:58 -0400586 // Whew! Hard work done.
587 synchronized (mLock) {
588 if (mIsLaunching) {
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400589 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Daniel Sandlerdca66122010-04-13 16:23:58 -0400590 }
591 }
592
Joe Onorato9c1289c2009-08-17 11:03:03 -0400593 // Load all apps if they're dirty
594 int allAppsSeq;
595 boolean allAppsDirty;
596 synchronized (mLock) {
597 allAppsSeq = mAllAppsSeq;
598 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800599 if (DEBUG_LOADERS) {
600 Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
601 + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
602 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400603 }
604 if (allAppsDirty) {
Daniel Sandlerdca66122010-04-13 16:23:58 -0400605 loadAndBindAllApps();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400606 }
607 synchronized (mLock) {
608 // If we're not stopped, and nobody has incremented mAllAppsSeq.
609 if (mStopped) {
610 return;
611 }
612 if (allAppsSeq == mAllAppsSeq) {
613 mLastAllAppsSeq = mAllAppsSeq;
614 }
615 }
616
Joe Onorato9c1289c2009-08-17 11:03:03 -0400617 // Clear out this reference, otherwise we end up holding it until all of the
618 // callback runnables are done.
619 mContext = null;
620
621 synchronized (mLock) {
622 // Setting the reference is atomic, but we can't do it inside the other critical
623 // sections.
624 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400625 }
626 }
627
628 public void stopLocked() {
629 synchronized (LoaderThread.this) {
630 mStopped = true;
631 this.notify();
632 }
633 }
634
635 /**
636 * Gets the callbacks object. If we've been stopped, or if the launcher object
Joe Onoratoc131b742010-03-11 15:45:05 -0800637 * has somehow been garbage collected, return null instead. Pass in the Callbacks
638 * object that was around when the deferred message was scheduled, and if there's
639 * a new Callbacks object around then also return null. This will save us from
640 * calling onto it with data that will be ignored.
Joe Onorato9c1289c2009-08-17 11:03:03 -0400641 */
Joe Onoratoc131b742010-03-11 15:45:05 -0800642 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400643 synchronized (mLock) {
644 if (mStopped) {
645 return null;
646 }
647
Joe Onoratoac033302010-04-13 17:19:18 -0700648 if (mCallbacks == null) {
649 return null;
650 }
651
Joe Onorato9c1289c2009-08-17 11:03:03 -0400652 final Callbacks callbacks = mCallbacks.get();
Joe Onoratoc131b742010-03-11 15:45:05 -0800653 if (callbacks != oldCallbacks) {
654 return null;
655 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400656 if (callbacks == null) {
657 Log.w(TAG, "no mCallbacks");
658 return null;
659 }
660
661 return callbacks;
662 }
663 }
664
665 private void loadWorkspace() {
666 long t = SystemClock.uptimeMillis();
667
668 final Context context = mContext;
669 final ContentResolver contentResolver = context.getContentResolver();
670 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800671 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800672 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400673
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400674 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500675 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800676 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400677
Romain Guy5c16f3e2010-01-12 17:24:58 -0800678 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
679
Joe Onorato9c1289c2009-08-17 11:03:03 -0400680 final Cursor c = contentResolver.query(
681 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
682
683 try {
684 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
685 final int intentIndex = c.getColumnIndexOrThrow
686 (LauncherSettings.Favorites.INTENT);
687 final int titleIndex = c.getColumnIndexOrThrow
688 (LauncherSettings.Favorites.TITLE);
689 final int iconTypeIndex = c.getColumnIndexOrThrow(
690 LauncherSettings.Favorites.ICON_TYPE);
691 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
692 final int iconPackageIndex = c.getColumnIndexOrThrow(
693 LauncherSettings.Favorites.ICON_PACKAGE);
694 final int iconResourceIndex = c.getColumnIndexOrThrow(
695 LauncherSettings.Favorites.ICON_RESOURCE);
696 final int containerIndex = c.getColumnIndexOrThrow(
697 LauncherSettings.Favorites.CONTAINER);
698 final int itemTypeIndex = c.getColumnIndexOrThrow(
699 LauncherSettings.Favorites.ITEM_TYPE);
700 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
701 LauncherSettings.Favorites.APPWIDGET_ID);
702 final int screenIndex = c.getColumnIndexOrThrow(
703 LauncherSettings.Favorites.SCREEN);
704 final int cellXIndex = c.getColumnIndexOrThrow
705 (LauncherSettings.Favorites.CELLX);
706 final int cellYIndex = c.getColumnIndexOrThrow
707 (LauncherSettings.Favorites.CELLY);
708 final int spanXIndex = c.getColumnIndexOrThrow
709 (LauncherSettings.Favorites.SPANX);
710 final int spanYIndex = c.getColumnIndexOrThrow(
711 LauncherSettings.Favorites.SPANY);
712 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
713 final int displayModeIndex = c.getColumnIndexOrThrow(
714 LauncherSettings.Favorites.DISPLAY_MODE);
715
Joe Onorato0589f0f2010-02-08 13:44:00 -0800716 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400717 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400718 LauncherAppWidgetInfo appWidgetInfo;
719 int container;
720 long id;
721 Intent intent;
722
723 while (!mStopped && c.moveToNext()) {
724 try {
725 int itemType = c.getInt(itemTypeIndex);
726
727 switch (itemType) {
728 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
729 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
730 intentDescription = c.getString(intentIndex);
731 try {
732 intent = Intent.parseUri(intentDescription, 0);
733 } catch (URISyntaxException e) {
734 continue;
735 }
736
737 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500738 info = getShortcutInfo(manager, intent, context, c, iconIndex,
739 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400740 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800741 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500742 iconPackageIndex, iconResourceIndex, iconIndex,
743 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400744 }
745
746 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500747 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400748
Joe Onorato56d82912010-03-07 14:32:10 -0500749 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400750 info.id = c.getLong(idIndex);
751 container = c.getInt(containerIndex);
752 info.container = container;
753 info.screen = c.getInt(screenIndex);
754 info.cellX = c.getInt(cellXIndex);
755 info.cellY = c.getInt(cellYIndex);
756
757 switch (container) {
758 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
759 mItems.add(info);
760 break;
761 default:
762 // Item is in a user folder
763 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500764 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400765 folderInfo.add(info);
766 break;
767 }
Joe Onorato56d82912010-03-07 14:32:10 -0500768 } else {
769 // Failed to load the shortcut, probably because the
770 // activity manager couldn't resolve it (maybe the app
771 // was uninstalled), or the db row was somehow screwed up.
772 // Delete it.
773 id = c.getLong(idIndex);
774 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
775 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
776 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400777 }
778 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400779
Joe Onoratoad72e172009-11-06 16:25:04 -0500780 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400781 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500782 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400783
784 folderInfo.title = c.getString(titleIndex);
785
786 folderInfo.id = id;
787 container = c.getInt(containerIndex);
788 folderInfo.container = container;
789 folderInfo.screen = c.getInt(screenIndex);
790 folderInfo.cellX = c.getInt(cellXIndex);
791 folderInfo.cellY = c.getInt(cellYIndex);
792
793 switch (container) {
794 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
795 mItems.add(folderInfo);
796 break;
797 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500798
799 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400800 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500801
Joe Onorato9c1289c2009-08-17 11:03:03 -0400802 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400803 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800804 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400805
Romain Guy5c16f3e2010-01-12 17:24:58 -0800806 // Make sure the live folder exists
807 final ProviderInfo providerInfo =
808 context.getPackageManager().resolveContentProvider(
809 uri.getAuthority(), 0);
810
811 if (providerInfo == null && !isSafeMode) {
812 itemsToRemove.add(id);
813 } else {
814 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
815
816 intentDescription = c.getString(intentIndex);
817 intent = null;
818 if (intentDescription != null) {
819 try {
820 intent = Intent.parseUri(intentDescription, 0);
821 } catch (URISyntaxException e) {
822 // Ignore, a live folder might not have a base intent
823 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400824 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800825
826 liveFolderInfo.title = c.getString(titleIndex);
827 liveFolderInfo.id = id;
828 liveFolderInfo.uri = uri;
829 container = c.getInt(containerIndex);
830 liveFolderInfo.container = container;
831 liveFolderInfo.screen = c.getInt(screenIndex);
832 liveFolderInfo.cellX = c.getInt(cellXIndex);
833 liveFolderInfo.cellY = c.getInt(cellYIndex);
834 liveFolderInfo.baseIntent = intent;
835 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
836
837 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
838 iconResourceIndex, liveFolderInfo);
839
840 switch (container) {
841 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
842 mItems.add(liveFolderInfo);
843 break;
844 }
845 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400846 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400847 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500848
Joe Onorato9c1289c2009-08-17 11:03:03 -0400849 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
850 // Read all Launcher-specific widget details
851 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800852 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400853
Romain Guy629de3e2010-01-13 12:20:59 -0800854 final AppWidgetProviderInfo provider =
855 widgets.getAppWidgetInfo(appWidgetId);
856
857 if (!isSafeMode && (provider == null || provider.provider == null ||
858 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700859 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
860 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800861 itemsToRemove.add(id);
862 } else {
863 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
864 appWidgetInfo.id = id;
865 appWidgetInfo.screen = c.getInt(screenIndex);
866 appWidgetInfo.cellX = c.getInt(cellXIndex);
867 appWidgetInfo.cellY = c.getInt(cellYIndex);
868 appWidgetInfo.spanX = c.getInt(spanXIndex);
869 appWidgetInfo.spanY = c.getInt(spanYIndex);
870
871 container = c.getInt(containerIndex);
872 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
873 Log.e(TAG, "Widget found where container "
874 + "!= CONTAINER_DESKTOP -- ignoring!");
875 continue;
876 }
877 appWidgetInfo.container = c.getInt(containerIndex);
878
879 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400880 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400881 break;
882 }
883 } catch (Exception e) {
884 Log.w(TAG, "Desktop items loading interrupted:", e);
885 }
886 }
887 } finally {
888 c.close();
889 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800890
891 if (itemsToRemove.size() > 0) {
892 ContentProviderClient client = contentResolver.acquireContentProviderClient(
893 LauncherSettings.Favorites.CONTENT_URI);
894 // Remove dead items
895 for (long id : itemsToRemove) {
896 if (DEBUG_LOADERS) {
897 Log.d(TAG, "Removed id = " + id);
898 }
899 // Don't notify content observers
900 try {
901 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
902 null, null);
903 } catch (RemoteException e) {
904 Log.w(TAG, "Could not remove id = " + id);
905 }
906 }
907 }
908
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800909 if (DEBUG_LOADERS) {
910 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
911 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400912 }
913
914 /**
915 * Read everything out of our database.
916 */
917 private void bindWorkspace() {
918 final long t = SystemClock.uptimeMillis();
919
920 // Don't use these two variables in any of the callback runnables.
921 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800922 final Callbacks oldCallbacks = mCallbacks.get();
923 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400924 // This launcher has exited and nobody bothered to tell us. Just bail.
925 Log.w(TAG, "LoaderThread running with no launcher");
926 return;
927 }
928
929 int N;
930 // Tell the workspace that we're about to start firing items at it
931 mHandler.post(new Runnable() {
932 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800933 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400934 if (callbacks != null) {
935 callbacks.startBinding();
936 }
937 }
938 });
939 // Add the items to the workspace.
940 N = mItems.size();
941 for (int i=0; i<N; i+=ITEMS_CHUNK) {
942 final int start = i;
943 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
944 mHandler.post(new Runnable() {
945 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800946 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400947 if (callbacks != null) {
948 callbacks.bindItems(mItems, start, start+chunkSize);
949 }
950 }
951 });
952 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500953 mHandler.post(new Runnable() {
954 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800955 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -0500956 if (callbacks != null) {
957 callbacks.bindFolders(mFolders);
958 }
959 }
960 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400961 // Wait until the queue goes empty.
962 mHandler.postIdle(new Runnable() {
963 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800964 if (DEBUG_LOADERS) {
965 Log.d(TAG, "Going to start binding widgets soon.");
966 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400967 }
968 });
969 // Bind the widgets, one at a time.
970 // WARNING: this is calling into the workspace from the background thread,
971 // but since getCurrentScreen() just returns the int, we should be okay. This
972 // is just a hint for the order, and if it's wrong, we'll be okay.
973 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -0800974 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400975 N = mAppWidgets.size();
976 // once for the current screen
977 for (int i=0; i<N; i++) {
978 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
979 if (widget.screen == currentScreen) {
980 mHandler.post(new Runnable() {
981 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800982 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400983 if (callbacks != null) {
984 callbacks.bindAppWidget(widget);
985 }
986 }
987 });
988 }
989 }
990 // once for the other screens
991 for (int i=0; i<N; i++) {
992 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
993 if (widget.screen != currentScreen) {
994 mHandler.post(new Runnable() {
995 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800996 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400997 if (callbacks != null) {
998 callbacks.bindAppWidget(widget);
999 }
1000 }
1001 });
1002 }
1003 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001004 // Tell the workspace that we're done.
1005 mHandler.post(new Runnable() {
1006 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001007 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001008 if (callbacks != null) {
1009 callbacks.finishBindingItems();
1010 }
1011 }
1012 });
1013 // If we're profiling, this is the last thing in the queue.
1014 mHandler.post(new Runnable() {
1015 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001016 if (DEBUG_LOADERS) {
1017 Log.d(TAG, "bound workspace in "
1018 + (SystemClock.uptimeMillis()-t) + "ms");
1019 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001020 if (Launcher.PROFILE_ROTATE) {
1021 android.os.Debug.stopMethodTracing();
1022 }
1023 }
1024 });
1025 }
1026
Daniel Sandlerdca66122010-04-13 16:23:58 -04001027 private void loadAndBindAllApps() {
1028 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1029
Joe Onoratod65d08e2010-04-20 15:43:37 -04001030 // Don't use these two variables in any of the callback runnables.
1031 // Otherwise we hold a reference to them.
1032 final Callbacks oldCallbacks = mCallbacks.get();
1033 if (oldCallbacks == null) {
1034 // This launcher has exited and nobody bothered to tell us. Just bail.
1035 Log.w(TAG, "LoaderThread running with no launcher (loadAndBindAllApps)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001036 return;
1037 }
1038
Joe Onoratod65d08e2010-04-20 15:43:37 -04001039 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1040 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1041
Joe Onorato0589f0f2010-02-08 13:44:00 -08001042 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001043 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001044
Joe Onoratod65d08e2010-04-20 15:43:37 -04001045 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001046
Joe Onoratod65d08e2010-04-20 15:43:37 -04001047 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001048 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001049 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001050 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001051 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001052 if (i == 0) {
1053 // This needs to happen inside the same lock block as when we
1054 // prepare the first batch for bindAllApplications. Otherwise
1055 // the package changed receiver can come in and double-add
1056 // (or miss one?).
1057 mAllAppsList.clear();
1058 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1059 apps = packageManager.queryIntentActivities(mainIntent, 0);
1060 if (DEBUG_LOADERS) {
1061 Log.d(TAG, "queryIntentActivities took "
1062 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1063 }
1064 if (apps == null) {
1065 return;
1066 }
1067 N = apps.size();
1068 if (DEBUG_LOADERS) {
1069 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1070 }
1071 if (N == 0) {
1072 // There are no apps?!?
1073 return;
1074 }
1075 if (mBatchSize == 0) {
1076 batchSize = N;
1077 } else {
1078 batchSize = mBatchSize;
1079 }
1080
1081 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1082 Collections.sort(apps,
1083 new ResolveInfo.DisplayNameComparator(packageManager));
1084 if (DEBUG_LOADERS) {
1085 Log.d(TAG, "sort took "
1086 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1087 }
1088 }
1089
Daniel Sandlerdca66122010-04-13 16:23:58 -04001090 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1091
Joe Onoratod65d08e2010-04-20 15:43:37 -04001092 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001093 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001094 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001095 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001096 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001097 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001098
1099 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001100 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001101 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1102 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1103
1104 mHandler.post(new Runnable() {
1105 public void run() {
1106 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001107 if (callbacks != null) {
1108 if (first) {
1109 mBeforeFirstLoad = false;
1110 callbacks.bindAllApplications(added);
1111 } else {
1112 callbacks.bindAppsAdded(added);
1113 }
1114 if (DEBUG_LOADERS) {
1115 Log.d(TAG, "bound " + added.size() + " apps in "
1116 + (SystemClock.uptimeMillis() - t) + "ms");
1117 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001118 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001119 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001120 }
1121 }
1122 });
1123
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001124 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001125 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001126 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001127 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001128 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001129
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001130 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001131 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001132 if (DEBUG_LOADERS) {
1133 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1134 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001135 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001136 } catch (InterruptedException exc) { }
1137 }
1138 }
1139
1140 if (DEBUG_LOADERS) {
1141 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001142 + (SystemClock.uptimeMillis()-t) + "ms"
1143 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001144 }
1145 }
1146
Joe Onoratobe386092009-11-17 17:32:16 -08001147 public void dumpState() {
1148 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1149 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1150 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1151 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1152 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1153 }
1154 }
1155
1156 public void dumpState() {
1157 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1158 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1159 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1160 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1161 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1162 if (mLoaderThread != null) {
1163 mLoaderThread.dumpState();
1164 } else {
1165 Log.d(TAG, "mLoader.mLoaderThread=null");
1166 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001167 }
1168 }
1169
1170 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001171 * This is called from the code that adds shortcuts from the intent receiver. This
1172 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001173 */
Joe Onorato56d82912010-03-07 14:32:10 -05001174 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001175 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001176 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001177
Joe Onorato56d82912010-03-07 14:32:10 -05001178 /**
1179 * Make an ShortcutInfo object for a shortcut that is an application.
1180 *
1181 * If c is not null, then it will be used to fill in missing data like the title and icon.
1182 */
1183 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1184 Cursor c, int iconIndex, int titleIndex) {
1185 Bitmap icon = null;
1186 final ShortcutInfo info = new ShortcutInfo();
1187
1188 ComponentName componentName = intent.getComponent();
1189 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001190 return null;
1191 }
1192
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001193 // TODO: See if the PackageManager knows about this case. If it doesn't
1194 // then return null & delete this.
1195
Joe Onorato56d82912010-03-07 14:32:10 -05001196 // the resource -- This may implicitly give us back the fallback icon,
1197 // but don't worry about that. All we're doing with usingFallbackIcon is
1198 // to avoid saving lots of copies of that in the database, and most apps
1199 // have icons anyway.
1200 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1201 if (resolveInfo != null) {
1202 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001203 }
Joe Onorato56d82912010-03-07 14:32:10 -05001204 // the db
1205 if (icon == null) {
1206 if (c != null) {
1207 icon = getIconFromCursor(c, iconIndex);
1208 }
1209 }
1210 // the fallback icon
1211 if (icon == null) {
1212 icon = getFallbackIcon();
1213 info.usingFallbackIcon = true;
1214 }
1215 info.setIcon(icon);
1216
1217 // from the resource
1218 if (resolveInfo != null) {
1219 info.title = resolveInfo.activityInfo.loadLabel(manager);
1220 }
1221 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001222 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001223 if (c != null) {
1224 info.title = c.getString(titleIndex);
1225 }
1226 }
1227 // fall back to the class name of the activity
1228 if (info.title == null) {
1229 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001230 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001231 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1232 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001233 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001234
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001235 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001236 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001237 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001238 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001239 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1240 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001241
Joe Onorato56d82912010-03-07 14:32:10 -05001242 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001243 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001244 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001245
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001246 // TODO: If there's an explicit component and we can't install that, delete it.
1247
Joe Onorato56d82912010-03-07 14:32:10 -05001248 info.title = c.getString(titleIndex);
1249
Joe Onorato9c1289c2009-08-17 11:03:03 -04001250 int iconType = c.getInt(iconTypeIndex);
1251 switch (iconType) {
1252 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1253 String packageName = c.getString(iconPackageIndex);
1254 String resourceName = c.getString(iconResourceIndex);
1255 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001256 info.customIcon = false;
1257 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001258 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001259 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001260 if (resources != null) {
1261 final int id = resources.getIdentifier(resourceName, null, null);
1262 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1263 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001264 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001265 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001266 }
Joe Onorato56d82912010-03-07 14:32:10 -05001267 // the db
1268 if (icon == null) {
1269 icon = getIconFromCursor(c, iconIndex);
1270 }
1271 // the fallback icon
1272 if (icon == null) {
1273 icon = getFallbackIcon();
1274 info.usingFallbackIcon = true;
1275 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001276 break;
1277 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001278 icon = getIconFromCursor(c, iconIndex);
1279 if (icon == null) {
1280 icon = getFallbackIcon();
1281 info.customIcon = false;
1282 info.usingFallbackIcon = true;
1283 } else {
1284 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001285 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001286 break;
1287 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001288 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001289 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001290 info.customIcon = false;
1291 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001292 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001293 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001294 return info;
1295 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001296
Joe Onorato56d82912010-03-07 14:32:10 -05001297 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1298 if (false) {
1299 Log.d(TAG, "getIconFromCursor app="
1300 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1301 }
1302 byte[] data = c.getBlob(iconIndex);
1303 try {
1304 return BitmapFactory.decodeByteArray(data, 0, data.length);
1305 } catch (Exception e) {
1306 return null;
1307 }
1308 }
1309
Joe Onorato0589f0f2010-02-08 13:44:00 -08001310 ShortcutInfo addShortcut(Context context, Intent data,
1311 CellLayout.CellInfo cellInfo, boolean notify) {
1312
1313 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1314 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1315 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1316
1317 return info;
1318 }
1319
1320 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1321 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1322 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1323 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1324
1325 Bitmap icon = null;
1326 boolean filtered = false;
1327 boolean customIcon = false;
1328 ShortcutIconResource iconResource = null;
1329
1330 if (bitmap != null && bitmap instanceof Bitmap) {
1331 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1332 filtered = true;
1333 customIcon = true;
1334 } else {
1335 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1336 if (extra != null && extra instanceof ShortcutIconResource) {
1337 try {
1338 iconResource = (ShortcutIconResource) extra;
1339 final PackageManager packageManager = context.getPackageManager();
1340 Resources resources = packageManager.getResourcesForApplication(
1341 iconResource.packageName);
1342 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1343 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1344 } catch (Exception e) {
1345 Log.w(TAG, "Could not load shortcut icon: " + extra);
1346 }
1347 }
1348 }
1349
Joe Onorato0589f0f2010-02-08 13:44:00 -08001350 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001351
1352 if (icon == null) {
1353 icon = getFallbackIcon();
1354 info.usingFallbackIcon = true;
1355 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001356 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001357
Joe Onorato0589f0f2010-02-08 13:44:00 -08001358 info.title = name;
1359 info.intent = intent;
1360 info.customIcon = customIcon;
1361 info.iconResource = iconResource;
1362
1363 return info;
1364 }
1365
Joe Onorato9c1289c2009-08-17 11:03:03 -04001366 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1367 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1368
1369 int iconType = c.getInt(iconTypeIndex);
1370 switch (iconType) {
1371 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1372 String packageName = c.getString(iconPackageIndex);
1373 String resourceName = c.getString(iconResourceIndex);
1374 PackageManager packageManager = context.getPackageManager();
1375 try {
1376 Resources resources = packageManager.getResourcesForApplication(packageName);
1377 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001378 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1379 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001380 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001381 liveFolderInfo.icon = Utilities.createIconBitmap(
1382 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1383 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001384 }
1385 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1386 liveFolderInfo.iconResource.packageName = packageName;
1387 liveFolderInfo.iconResource.resourceName = resourceName;
1388 break;
1389 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001390 liveFolderInfo.icon = Utilities.createIconBitmap(
1391 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1392 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001393 }
1394 }
1395
Joe Onorato56d82912010-03-07 14:32:10 -05001396 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1397 // If this icon doesn't have a custom icon, check to see
1398 // what's stored in the DB, and if it doesn't match what
1399 // we're going to show, store what we are going to show back
1400 // into the DB. We do this so when we're loading, if the
1401 // package manager can't find an icon (for example because
1402 // the app is on SD) then we can use that instead.
1403 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1404 boolean needSave;
1405 byte[] data = c.getBlob(iconIndex);
1406 try {
1407 if (data != null) {
1408 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1409 Bitmap loaded = info.getIcon(mIconCache);
1410 needSave = !saved.sameAs(loaded);
1411 } else {
1412 needSave = true;
1413 }
1414 } catch (Exception e) {
1415 needSave = true;
1416 }
1417 if (needSave) {
1418 Log.d(TAG, "going to save icon bitmap for info=" + info);
1419 // This is slower than is ideal, but this only happens either
1420 // after the froyo OTA or when the app is updated with a new
1421 // icon.
1422 updateItemInDatabase(context, info);
1423 }
1424 }
1425 }
1426
Joe Onorato9c1289c2009-08-17 11:03:03 -04001427 /**
1428 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1429 * or make a new one.
1430 */
1431 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1432 // See if a placeholder was created for us already
1433 FolderInfo folderInfo = folders.get(id);
1434 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1435 // No placeholder -- create a new instance
1436 folderInfo = new UserFolderInfo();
1437 folders.put(id, folderInfo);
1438 }
1439 return (UserFolderInfo) folderInfo;
1440 }
1441
1442 /**
1443 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1444 * new one.
1445 */
1446 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1447 // See if a placeholder was created for us already
1448 FolderInfo folderInfo = folders.get(id);
1449 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1450 // No placeholder -- create a new instance
1451 folderInfo = new LiveFolderInfo();
1452 folders.put(id, folderInfo);
1453 }
1454 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001455 }
1456
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001457 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1458 String label = activityInfo.loadLabel(manager).toString();
1459 if (label == null) {
1460 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1461 if (label == null) {
1462 label = activityInfo.name;
1463 }
1464 }
1465 return label;
1466 }
1467
Joe Onorato9c1289c2009-08-17 11:03:03 -04001468 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001469 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001470 = new Comparator<ApplicationInfo>() {
1471 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1472 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001473 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001474 };
Joe Onoratobe386092009-11-17 17:32:16 -08001475
1476 public void dumpState() {
1477 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1478 Log.d(TAG, "mCallbacks=" + mCallbacks);
1479 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1480 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1481 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1482 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1483 mLoader.dumpState();
1484 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001485}