blob: 6127649847e8af8ea190fbddf35f01ced5b7d854 [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 }
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700626
627 // Trigger a gc to try to clean up after the stuff is done, since the
628 // renderscript allocations aren't charge to the java heap.
629 mHandler.post(new Runnable() {
630 public void run() {
631 System.gc();
632 }
633 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400634 }
635
636 public void stopLocked() {
637 synchronized (LoaderThread.this) {
638 mStopped = true;
639 this.notify();
640 }
641 }
642
643 /**
644 * Gets the callbacks object. If we've been stopped, or if the launcher object
Joe Onoratoc131b742010-03-11 15:45:05 -0800645 * has somehow been garbage collected, return null instead. Pass in the Callbacks
646 * object that was around when the deferred message was scheduled, and if there's
647 * a new Callbacks object around then also return null. This will save us from
648 * calling onto it with data that will be ignored.
Joe Onorato9c1289c2009-08-17 11:03:03 -0400649 */
Joe Onoratoc131b742010-03-11 15:45:05 -0800650 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400651 synchronized (mLock) {
652 if (mStopped) {
653 return null;
654 }
655
Joe Onoratoac033302010-04-13 17:19:18 -0700656 if (mCallbacks == null) {
657 return null;
658 }
659
Joe Onorato9c1289c2009-08-17 11:03:03 -0400660 final Callbacks callbacks = mCallbacks.get();
Joe Onoratoc131b742010-03-11 15:45:05 -0800661 if (callbacks != oldCallbacks) {
662 return null;
663 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400664 if (callbacks == null) {
665 Log.w(TAG, "no mCallbacks");
666 return null;
667 }
668
669 return callbacks;
670 }
671 }
672
673 private void loadWorkspace() {
674 long t = SystemClock.uptimeMillis();
675
676 final Context context = mContext;
677 final ContentResolver contentResolver = context.getContentResolver();
678 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800679 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800680 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400681
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400682 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500683 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800684 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400685
Romain Guy5c16f3e2010-01-12 17:24:58 -0800686 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
687
Joe Onorato9c1289c2009-08-17 11:03:03 -0400688 final Cursor c = contentResolver.query(
689 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
690
691 try {
692 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
693 final int intentIndex = c.getColumnIndexOrThrow
694 (LauncherSettings.Favorites.INTENT);
695 final int titleIndex = c.getColumnIndexOrThrow
696 (LauncherSettings.Favorites.TITLE);
697 final int iconTypeIndex = c.getColumnIndexOrThrow(
698 LauncherSettings.Favorites.ICON_TYPE);
699 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
700 final int iconPackageIndex = c.getColumnIndexOrThrow(
701 LauncherSettings.Favorites.ICON_PACKAGE);
702 final int iconResourceIndex = c.getColumnIndexOrThrow(
703 LauncherSettings.Favorites.ICON_RESOURCE);
704 final int containerIndex = c.getColumnIndexOrThrow(
705 LauncherSettings.Favorites.CONTAINER);
706 final int itemTypeIndex = c.getColumnIndexOrThrow(
707 LauncherSettings.Favorites.ITEM_TYPE);
708 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
709 LauncherSettings.Favorites.APPWIDGET_ID);
710 final int screenIndex = c.getColumnIndexOrThrow(
711 LauncherSettings.Favorites.SCREEN);
712 final int cellXIndex = c.getColumnIndexOrThrow
713 (LauncherSettings.Favorites.CELLX);
714 final int cellYIndex = c.getColumnIndexOrThrow
715 (LauncherSettings.Favorites.CELLY);
716 final int spanXIndex = c.getColumnIndexOrThrow
717 (LauncherSettings.Favorites.SPANX);
718 final int spanYIndex = c.getColumnIndexOrThrow(
719 LauncherSettings.Favorites.SPANY);
720 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
721 final int displayModeIndex = c.getColumnIndexOrThrow(
722 LauncherSettings.Favorites.DISPLAY_MODE);
723
Joe Onorato0589f0f2010-02-08 13:44:00 -0800724 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400725 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400726 LauncherAppWidgetInfo appWidgetInfo;
727 int container;
728 long id;
729 Intent intent;
730
731 while (!mStopped && c.moveToNext()) {
732 try {
733 int itemType = c.getInt(itemTypeIndex);
734
735 switch (itemType) {
736 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
737 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
738 intentDescription = c.getString(intentIndex);
739 try {
740 intent = Intent.parseUri(intentDescription, 0);
741 } catch (URISyntaxException e) {
742 continue;
743 }
744
745 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500746 info = getShortcutInfo(manager, intent, context, c, iconIndex,
747 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400748 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800749 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500750 iconPackageIndex, iconResourceIndex, iconIndex,
751 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400752 }
753
754 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500755 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400756
Joe Onorato56d82912010-03-07 14:32:10 -0500757 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400758 info.id = c.getLong(idIndex);
759 container = c.getInt(containerIndex);
760 info.container = container;
761 info.screen = c.getInt(screenIndex);
762 info.cellX = c.getInt(cellXIndex);
763 info.cellY = c.getInt(cellYIndex);
764
765 switch (container) {
766 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
767 mItems.add(info);
768 break;
769 default:
770 // Item is in a user folder
771 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500772 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400773 folderInfo.add(info);
774 break;
775 }
Joe Onorato56d82912010-03-07 14:32:10 -0500776 } else {
777 // Failed to load the shortcut, probably because the
778 // activity manager couldn't resolve it (maybe the app
779 // was uninstalled), or the db row was somehow screwed up.
780 // Delete it.
781 id = c.getLong(idIndex);
782 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
783 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
784 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400785 }
786 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400787
Joe Onoratoad72e172009-11-06 16:25:04 -0500788 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400789 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500790 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400791
792 folderInfo.title = c.getString(titleIndex);
793
794 folderInfo.id = id;
795 container = c.getInt(containerIndex);
796 folderInfo.container = container;
797 folderInfo.screen = c.getInt(screenIndex);
798 folderInfo.cellX = c.getInt(cellXIndex);
799 folderInfo.cellY = c.getInt(cellYIndex);
800
801 switch (container) {
802 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
803 mItems.add(folderInfo);
804 break;
805 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500806
807 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400808 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500809
Joe Onorato9c1289c2009-08-17 11:03:03 -0400810 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400811 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800812 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400813
Romain Guy5c16f3e2010-01-12 17:24:58 -0800814 // Make sure the live folder exists
815 final ProviderInfo providerInfo =
816 context.getPackageManager().resolveContentProvider(
817 uri.getAuthority(), 0);
818
819 if (providerInfo == null && !isSafeMode) {
820 itemsToRemove.add(id);
821 } else {
822 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
823
824 intentDescription = c.getString(intentIndex);
825 intent = null;
826 if (intentDescription != null) {
827 try {
828 intent = Intent.parseUri(intentDescription, 0);
829 } catch (URISyntaxException e) {
830 // Ignore, a live folder might not have a base intent
831 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400832 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800833
834 liveFolderInfo.title = c.getString(titleIndex);
835 liveFolderInfo.id = id;
836 liveFolderInfo.uri = uri;
837 container = c.getInt(containerIndex);
838 liveFolderInfo.container = container;
839 liveFolderInfo.screen = c.getInt(screenIndex);
840 liveFolderInfo.cellX = c.getInt(cellXIndex);
841 liveFolderInfo.cellY = c.getInt(cellYIndex);
842 liveFolderInfo.baseIntent = intent;
843 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
844
845 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
846 iconResourceIndex, liveFolderInfo);
847
848 switch (container) {
849 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
850 mItems.add(liveFolderInfo);
851 break;
852 }
853 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400854 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400855 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500856
Joe Onorato9c1289c2009-08-17 11:03:03 -0400857 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
858 // Read all Launcher-specific widget details
859 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800860 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400861
Romain Guy629de3e2010-01-13 12:20:59 -0800862 final AppWidgetProviderInfo provider =
863 widgets.getAppWidgetInfo(appWidgetId);
864
865 if (!isSafeMode && (provider == null || provider.provider == null ||
866 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700867 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
868 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800869 itemsToRemove.add(id);
870 } else {
871 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
872 appWidgetInfo.id = id;
873 appWidgetInfo.screen = c.getInt(screenIndex);
874 appWidgetInfo.cellX = c.getInt(cellXIndex);
875 appWidgetInfo.cellY = c.getInt(cellYIndex);
876 appWidgetInfo.spanX = c.getInt(spanXIndex);
877 appWidgetInfo.spanY = c.getInt(spanYIndex);
878
879 container = c.getInt(containerIndex);
880 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
881 Log.e(TAG, "Widget found where container "
882 + "!= CONTAINER_DESKTOP -- ignoring!");
883 continue;
884 }
885 appWidgetInfo.container = c.getInt(containerIndex);
886
887 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400888 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400889 break;
890 }
891 } catch (Exception e) {
892 Log.w(TAG, "Desktop items loading interrupted:", e);
893 }
894 }
895 } finally {
896 c.close();
897 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800898
899 if (itemsToRemove.size() > 0) {
900 ContentProviderClient client = contentResolver.acquireContentProviderClient(
901 LauncherSettings.Favorites.CONTENT_URI);
902 // Remove dead items
903 for (long id : itemsToRemove) {
904 if (DEBUG_LOADERS) {
905 Log.d(TAG, "Removed id = " + id);
906 }
907 // Don't notify content observers
908 try {
909 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
910 null, null);
911 } catch (RemoteException e) {
912 Log.w(TAG, "Could not remove id = " + id);
913 }
914 }
915 }
916
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800917 if (DEBUG_LOADERS) {
918 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
919 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400920 }
921
922 /**
923 * Read everything out of our database.
924 */
925 private void bindWorkspace() {
926 final long t = SystemClock.uptimeMillis();
927
928 // Don't use these two variables in any of the callback runnables.
929 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800930 final Callbacks oldCallbacks = mCallbacks.get();
931 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400932 // This launcher has exited and nobody bothered to tell us. Just bail.
933 Log.w(TAG, "LoaderThread running with no launcher");
934 return;
935 }
936
937 int N;
938 // Tell the workspace that we're about to start firing items at it
939 mHandler.post(new Runnable() {
940 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800941 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400942 if (callbacks != null) {
943 callbacks.startBinding();
944 }
945 }
946 });
947 // Add the items to the workspace.
948 N = mItems.size();
949 for (int i=0; i<N; i+=ITEMS_CHUNK) {
950 final int start = i;
951 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
952 mHandler.post(new Runnable() {
953 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800954 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400955 if (callbacks != null) {
956 callbacks.bindItems(mItems, start, start+chunkSize);
957 }
958 }
959 });
960 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500961 mHandler.post(new Runnable() {
962 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800963 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -0500964 if (callbacks != null) {
965 callbacks.bindFolders(mFolders);
966 }
967 }
968 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400969 // Wait until the queue goes empty.
970 mHandler.postIdle(new Runnable() {
971 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800972 if (DEBUG_LOADERS) {
973 Log.d(TAG, "Going to start binding widgets soon.");
974 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400975 }
976 });
977 // Bind the widgets, one at a time.
978 // WARNING: this is calling into the workspace from the background thread,
979 // but since getCurrentScreen() just returns the int, we should be okay. This
980 // is just a hint for the order, and if it's wrong, we'll be okay.
981 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -0800982 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400983 N = mAppWidgets.size();
984 // once for the current screen
985 for (int i=0; i<N; i++) {
986 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
987 if (widget.screen == currentScreen) {
988 mHandler.post(new Runnable() {
989 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800990 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400991 if (callbacks != null) {
992 callbacks.bindAppWidget(widget);
993 }
994 }
995 });
996 }
997 }
998 // once for the other screens
999 for (int i=0; i<N; i++) {
1000 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1001 if (widget.screen != currentScreen) {
1002 mHandler.post(new Runnable() {
1003 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001004 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001005 if (callbacks != null) {
1006 callbacks.bindAppWidget(widget);
1007 }
1008 }
1009 });
1010 }
1011 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012 // Tell the workspace that we're done.
1013 mHandler.post(new Runnable() {
1014 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001015 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001016 if (callbacks != null) {
1017 callbacks.finishBindingItems();
1018 }
1019 }
1020 });
1021 // If we're profiling, this is the last thing in the queue.
1022 mHandler.post(new Runnable() {
1023 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001024 if (DEBUG_LOADERS) {
1025 Log.d(TAG, "bound workspace in "
1026 + (SystemClock.uptimeMillis()-t) + "ms");
1027 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001028 if (Launcher.PROFILE_ROTATE) {
1029 android.os.Debug.stopMethodTracing();
1030 }
1031 }
1032 });
1033 }
1034
Daniel Sandlerdca66122010-04-13 16:23:58 -04001035 private void loadAndBindAllApps() {
1036 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1037
Joe Onoratod65d08e2010-04-20 15:43:37 -04001038 // Don't use these two variables in any of the callback runnables.
1039 // Otherwise we hold a reference to them.
1040 final Callbacks oldCallbacks = mCallbacks.get();
1041 if (oldCallbacks == null) {
1042 // This launcher has exited and nobody bothered to tell us. Just bail.
1043 Log.w(TAG, "LoaderThread running with no launcher (loadAndBindAllApps)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001044 return;
1045 }
1046
Joe Onoratod65d08e2010-04-20 15:43:37 -04001047 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1048 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1049
Joe Onorato0589f0f2010-02-08 13:44:00 -08001050 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001051 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001052
Joe Onoratod65d08e2010-04-20 15:43:37 -04001053 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001054
Joe Onoratod65d08e2010-04-20 15:43:37 -04001055 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001056 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001057 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001058 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001059 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001060 if (i == 0) {
1061 // This needs to happen inside the same lock block as when we
1062 // prepare the first batch for bindAllApplications. Otherwise
1063 // the package changed receiver can come in and double-add
1064 // (or miss one?).
1065 mAllAppsList.clear();
1066 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1067 apps = packageManager.queryIntentActivities(mainIntent, 0);
1068 if (DEBUG_LOADERS) {
1069 Log.d(TAG, "queryIntentActivities took "
1070 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1071 }
1072 if (apps == null) {
1073 return;
1074 }
1075 N = apps.size();
1076 if (DEBUG_LOADERS) {
1077 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1078 }
1079 if (N == 0) {
1080 // There are no apps?!?
1081 return;
1082 }
1083 if (mBatchSize == 0) {
1084 batchSize = N;
1085 } else {
1086 batchSize = mBatchSize;
1087 }
1088
1089 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1090 Collections.sort(apps,
1091 new ResolveInfo.DisplayNameComparator(packageManager));
1092 if (DEBUG_LOADERS) {
1093 Log.d(TAG, "sort took "
1094 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1095 }
1096 }
1097
Daniel Sandlerdca66122010-04-13 16:23:58 -04001098 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1099
Joe Onoratod65d08e2010-04-20 15:43:37 -04001100 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001101 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001102 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001103 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001104 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001105 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001106
1107 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001108 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001109 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1110 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1111
1112 mHandler.post(new Runnable() {
1113 public void run() {
1114 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001115 if (callbacks != null) {
1116 if (first) {
1117 mBeforeFirstLoad = false;
1118 callbacks.bindAllApplications(added);
1119 } else {
1120 callbacks.bindAppsAdded(added);
1121 }
1122 if (DEBUG_LOADERS) {
1123 Log.d(TAG, "bound " + added.size() + " apps in "
1124 + (SystemClock.uptimeMillis() - t) + "ms");
1125 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001126 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001127 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001128 }
1129 }
1130 });
1131
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001132 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001133 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001134 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001135 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001136 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001137
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001138 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001139 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001140 if (DEBUG_LOADERS) {
1141 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1142 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001143 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001144 } catch (InterruptedException exc) { }
1145 }
1146 }
1147
1148 if (DEBUG_LOADERS) {
1149 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001150 + (SystemClock.uptimeMillis()-t) + "ms"
1151 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001152 }
1153 }
1154
Joe Onoratobe386092009-11-17 17:32:16 -08001155 public void dumpState() {
1156 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1157 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1158 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1159 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1160 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1161 }
1162 }
1163
1164 public void dumpState() {
1165 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1166 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1167 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1168 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1169 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1170 if (mLoaderThread != null) {
1171 mLoaderThread.dumpState();
1172 } else {
1173 Log.d(TAG, "mLoader.mLoaderThread=null");
1174 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001175 }
1176 }
1177
1178 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001179 * This is called from the code that adds shortcuts from the intent receiver. This
1180 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001181 */
Joe Onorato56d82912010-03-07 14:32:10 -05001182 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001183 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001184 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001185
Joe Onorato56d82912010-03-07 14:32:10 -05001186 /**
1187 * Make an ShortcutInfo object for a shortcut that is an application.
1188 *
1189 * If c is not null, then it will be used to fill in missing data like the title and icon.
1190 */
1191 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1192 Cursor c, int iconIndex, int titleIndex) {
1193 Bitmap icon = null;
1194 final ShortcutInfo info = new ShortcutInfo();
1195
1196 ComponentName componentName = intent.getComponent();
1197 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001198 return null;
1199 }
1200
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001201 // TODO: See if the PackageManager knows about this case. If it doesn't
1202 // then return null & delete this.
1203
Joe Onorato56d82912010-03-07 14:32:10 -05001204 // the resource -- This may implicitly give us back the fallback icon,
1205 // but don't worry about that. All we're doing with usingFallbackIcon is
1206 // to avoid saving lots of copies of that in the database, and most apps
1207 // have icons anyway.
1208 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1209 if (resolveInfo != null) {
1210 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001211 }
Joe Onorato56d82912010-03-07 14:32:10 -05001212 // the db
1213 if (icon == null) {
1214 if (c != null) {
1215 icon = getIconFromCursor(c, iconIndex);
1216 }
1217 }
1218 // the fallback icon
1219 if (icon == null) {
1220 icon = getFallbackIcon();
1221 info.usingFallbackIcon = true;
1222 }
1223 info.setIcon(icon);
1224
1225 // from the resource
1226 if (resolveInfo != null) {
1227 info.title = resolveInfo.activityInfo.loadLabel(manager);
1228 }
1229 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001230 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001231 if (c != null) {
1232 info.title = c.getString(titleIndex);
1233 }
1234 }
1235 // fall back to the class name of the activity
1236 if (info.title == null) {
1237 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001238 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001239 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1240 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001241 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001242
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001243 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001244 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001245 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001246 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001247 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1248 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001249
Joe Onorato56d82912010-03-07 14:32:10 -05001250 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001251 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001252 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001253
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001254 // TODO: If there's an explicit component and we can't install that, delete it.
1255
Joe Onorato56d82912010-03-07 14:32:10 -05001256 info.title = c.getString(titleIndex);
1257
Joe Onorato9c1289c2009-08-17 11:03:03 -04001258 int iconType = c.getInt(iconTypeIndex);
1259 switch (iconType) {
1260 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1261 String packageName = c.getString(iconPackageIndex);
1262 String resourceName = c.getString(iconResourceIndex);
1263 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001264 info.customIcon = false;
1265 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001266 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001267 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001268 if (resources != null) {
1269 final int id = resources.getIdentifier(resourceName, null, null);
1270 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1271 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001272 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001273 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001274 }
Joe Onorato56d82912010-03-07 14:32:10 -05001275 // the db
1276 if (icon == null) {
1277 icon = getIconFromCursor(c, iconIndex);
1278 }
1279 // the fallback icon
1280 if (icon == null) {
1281 icon = getFallbackIcon();
1282 info.usingFallbackIcon = true;
1283 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001284 break;
1285 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001286 icon = getIconFromCursor(c, iconIndex);
1287 if (icon == null) {
1288 icon = getFallbackIcon();
1289 info.customIcon = false;
1290 info.usingFallbackIcon = true;
1291 } else {
1292 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001293 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001294 break;
1295 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001296 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001297 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001298 info.customIcon = false;
1299 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001300 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001301 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001302 return info;
1303 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001304
Joe Onorato56d82912010-03-07 14:32:10 -05001305 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1306 if (false) {
1307 Log.d(TAG, "getIconFromCursor app="
1308 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1309 }
1310 byte[] data = c.getBlob(iconIndex);
1311 try {
1312 return BitmapFactory.decodeByteArray(data, 0, data.length);
1313 } catch (Exception e) {
1314 return null;
1315 }
1316 }
1317
Joe Onorato0589f0f2010-02-08 13:44:00 -08001318 ShortcutInfo addShortcut(Context context, Intent data,
1319 CellLayout.CellInfo cellInfo, boolean notify) {
1320
1321 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1322 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1323 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1324
1325 return info;
1326 }
1327
1328 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1329 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1330 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1331 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1332
1333 Bitmap icon = null;
1334 boolean filtered = false;
1335 boolean customIcon = false;
1336 ShortcutIconResource iconResource = null;
1337
1338 if (bitmap != null && bitmap instanceof Bitmap) {
1339 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1340 filtered = true;
1341 customIcon = true;
1342 } else {
1343 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1344 if (extra != null && extra instanceof ShortcutIconResource) {
1345 try {
1346 iconResource = (ShortcutIconResource) extra;
1347 final PackageManager packageManager = context.getPackageManager();
1348 Resources resources = packageManager.getResourcesForApplication(
1349 iconResource.packageName);
1350 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1351 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1352 } catch (Exception e) {
1353 Log.w(TAG, "Could not load shortcut icon: " + extra);
1354 }
1355 }
1356 }
1357
Joe Onorato0589f0f2010-02-08 13:44:00 -08001358 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001359
1360 if (icon == null) {
1361 icon = getFallbackIcon();
1362 info.usingFallbackIcon = true;
1363 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001364 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001365
Joe Onorato0589f0f2010-02-08 13:44:00 -08001366 info.title = name;
1367 info.intent = intent;
1368 info.customIcon = customIcon;
1369 info.iconResource = iconResource;
1370
1371 return info;
1372 }
1373
Joe Onorato9c1289c2009-08-17 11:03:03 -04001374 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1375 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1376
1377 int iconType = c.getInt(iconTypeIndex);
1378 switch (iconType) {
1379 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1380 String packageName = c.getString(iconPackageIndex);
1381 String resourceName = c.getString(iconResourceIndex);
1382 PackageManager packageManager = context.getPackageManager();
1383 try {
1384 Resources resources = packageManager.getResourcesForApplication(packageName);
1385 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001386 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1387 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001388 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001389 liveFolderInfo.icon = Utilities.createIconBitmap(
1390 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1391 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001392 }
1393 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1394 liveFolderInfo.iconResource.packageName = packageName;
1395 liveFolderInfo.iconResource.resourceName = resourceName;
1396 break;
1397 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001398 liveFolderInfo.icon = Utilities.createIconBitmap(
1399 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1400 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001401 }
1402 }
1403
Joe Onorato56d82912010-03-07 14:32:10 -05001404 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1405 // If this icon doesn't have a custom icon, check to see
1406 // what's stored in the DB, and if it doesn't match what
1407 // we're going to show, store what we are going to show back
1408 // into the DB. We do this so when we're loading, if the
1409 // package manager can't find an icon (for example because
1410 // the app is on SD) then we can use that instead.
1411 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1412 boolean needSave;
1413 byte[] data = c.getBlob(iconIndex);
1414 try {
1415 if (data != null) {
1416 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1417 Bitmap loaded = info.getIcon(mIconCache);
1418 needSave = !saved.sameAs(loaded);
1419 } else {
1420 needSave = true;
1421 }
1422 } catch (Exception e) {
1423 needSave = true;
1424 }
1425 if (needSave) {
1426 Log.d(TAG, "going to save icon bitmap for info=" + info);
1427 // This is slower than is ideal, but this only happens either
1428 // after the froyo OTA or when the app is updated with a new
1429 // icon.
1430 updateItemInDatabase(context, info);
1431 }
1432 }
1433 }
1434
Joe Onorato9c1289c2009-08-17 11:03:03 -04001435 /**
1436 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1437 * or make a new one.
1438 */
1439 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1440 // See if a placeholder was created for us already
1441 FolderInfo folderInfo = folders.get(id);
1442 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1443 // No placeholder -- create a new instance
1444 folderInfo = new UserFolderInfo();
1445 folders.put(id, folderInfo);
1446 }
1447 return (UserFolderInfo) folderInfo;
1448 }
1449
1450 /**
1451 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1452 * new one.
1453 */
1454 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1455 // See if a placeholder was created for us already
1456 FolderInfo folderInfo = folders.get(id);
1457 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1458 // No placeholder -- create a new instance
1459 folderInfo = new LiveFolderInfo();
1460 folders.put(id, folderInfo);
1461 }
1462 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001463 }
1464
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001465 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1466 String label = activityInfo.loadLabel(manager).toString();
1467 if (label == null) {
1468 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1469 if (label == null) {
1470 label = activityInfo.name;
1471 }
1472 }
1473 return label;
1474 }
1475
Joe Onorato9c1289c2009-08-17 11:03:03 -04001476 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001477 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001478 = new Comparator<ApplicationInfo>() {
1479 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1480 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001481 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001482 };
Joe Onoratobe386092009-11-17 17:32:16 -08001483
1484 public void dumpState() {
1485 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1486 Log.d(TAG, "mCallbacks=" + mCallbacks);
1487 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1488 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1489 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1490 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1491 mLoader.dumpState();
1492 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001493}