blob: d2cebc0d4c4c630c7d387340eadbc0f58dc6ba63 [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
Daniel Sandler8802e962010-05-26 16:28:16 -0400673 // check & update map of what's occupied; used to discard overlapping/invalid items
674 private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
Daniel Sandler596c0e62010-06-02 13:29:52 -0700675 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
676 return true;
677 }
678
Daniel Sandler8802e962010-05-26 16:28:16 -0400679 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
680 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
681 if (occupied[item.screen][x][y] != null) {
682 Log.e(TAG, "Error loading shortcut " + item
683 + " into cell (" + item.screen + ":"
684 + x + "," + y
685 + ") occupied by "
686 + occupied[item.screen][x][y]);
687 return false;
688 }
689 }
690 }
691 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
692 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
693 occupied[item.screen][x][y] = item;
694 }
695 }
696 return true;
697 }
698
Joe Onorato9c1289c2009-08-17 11:03:03 -0400699 private void loadWorkspace() {
700 long t = SystemClock.uptimeMillis();
701
702 final Context context = mContext;
703 final ContentResolver contentResolver = context.getContentResolver();
704 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800705 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800706 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400707
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400708 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500709 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800710 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400711
Romain Guy5c16f3e2010-01-12 17:24:58 -0800712 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
713
Joe Onorato9c1289c2009-08-17 11:03:03 -0400714 final Cursor c = contentResolver.query(
715 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
716
Daniel Sandler8802e962010-05-26 16:28:16 -0400717 final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
718
Joe Onorato9c1289c2009-08-17 11:03:03 -0400719 try {
720 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
721 final int intentIndex = c.getColumnIndexOrThrow
722 (LauncherSettings.Favorites.INTENT);
723 final int titleIndex = c.getColumnIndexOrThrow
724 (LauncherSettings.Favorites.TITLE);
725 final int iconTypeIndex = c.getColumnIndexOrThrow(
726 LauncherSettings.Favorites.ICON_TYPE);
727 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
728 final int iconPackageIndex = c.getColumnIndexOrThrow(
729 LauncherSettings.Favorites.ICON_PACKAGE);
730 final int iconResourceIndex = c.getColumnIndexOrThrow(
731 LauncherSettings.Favorites.ICON_RESOURCE);
732 final int containerIndex = c.getColumnIndexOrThrow(
733 LauncherSettings.Favorites.CONTAINER);
734 final int itemTypeIndex = c.getColumnIndexOrThrow(
735 LauncherSettings.Favorites.ITEM_TYPE);
736 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
737 LauncherSettings.Favorites.APPWIDGET_ID);
738 final int screenIndex = c.getColumnIndexOrThrow(
739 LauncherSettings.Favorites.SCREEN);
740 final int cellXIndex = c.getColumnIndexOrThrow
741 (LauncherSettings.Favorites.CELLX);
742 final int cellYIndex = c.getColumnIndexOrThrow
743 (LauncherSettings.Favorites.CELLY);
744 final int spanXIndex = c.getColumnIndexOrThrow
745 (LauncherSettings.Favorites.SPANX);
746 final int spanYIndex = c.getColumnIndexOrThrow(
747 LauncherSettings.Favorites.SPANY);
748 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
749 final int displayModeIndex = c.getColumnIndexOrThrow(
750 LauncherSettings.Favorites.DISPLAY_MODE);
751
Joe Onorato0589f0f2010-02-08 13:44:00 -0800752 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400753 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400754 LauncherAppWidgetInfo appWidgetInfo;
755 int container;
756 long id;
757 Intent intent;
758
759 while (!mStopped && c.moveToNext()) {
760 try {
761 int itemType = c.getInt(itemTypeIndex);
762
763 switch (itemType) {
764 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
765 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
766 intentDescription = c.getString(intentIndex);
767 try {
768 intent = Intent.parseUri(intentDescription, 0);
769 } catch (URISyntaxException e) {
770 continue;
771 }
772
773 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500774 info = getShortcutInfo(manager, intent, context, c, iconIndex,
775 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400776 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800777 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500778 iconPackageIndex, iconResourceIndex, iconIndex,
779 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400780 }
781
782 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500783 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400784
Joe Onorato56d82912010-03-07 14:32:10 -0500785 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400786 info.id = c.getLong(idIndex);
787 container = c.getInt(containerIndex);
788 info.container = container;
789 info.screen = c.getInt(screenIndex);
790 info.cellX = c.getInt(cellXIndex);
791 info.cellY = c.getInt(cellYIndex);
792
Daniel Sandler8802e962010-05-26 16:28:16 -0400793 // check & update map of what's occupied
794 if (!checkItemPlacement(occupied, info)) {
795 break;
796 }
797
Joe Onorato9c1289c2009-08-17 11:03:03 -0400798 switch (container) {
799 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
800 mItems.add(info);
801 break;
802 default:
803 // Item is in a user folder
804 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500805 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400806 folderInfo.add(info);
807 break;
808 }
Joe Onorato56d82912010-03-07 14:32:10 -0500809 } else {
810 // Failed to load the shortcut, probably because the
811 // activity manager couldn't resolve it (maybe the app
812 // was uninstalled), or the db row was somehow screwed up.
813 // Delete it.
814 id = c.getLong(idIndex);
815 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
816 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
817 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400818 }
819 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400820
Joe Onoratoad72e172009-11-06 16:25:04 -0500821 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400822 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500823 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400824
825 folderInfo.title = c.getString(titleIndex);
826
827 folderInfo.id = id;
828 container = c.getInt(containerIndex);
829 folderInfo.container = container;
830 folderInfo.screen = c.getInt(screenIndex);
831 folderInfo.cellX = c.getInt(cellXIndex);
832 folderInfo.cellY = c.getInt(cellYIndex);
833
Daniel Sandler8802e962010-05-26 16:28:16 -0400834 // check & update map of what's occupied
835 if (!checkItemPlacement(occupied, folderInfo)) {
836 break;
837 }
838
Joe Onorato9c1289c2009-08-17 11:03:03 -0400839 switch (container) {
840 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
841 mItems.add(folderInfo);
842 break;
843 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500844
845 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400846 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500847
Joe Onorato9c1289c2009-08-17 11:03:03 -0400848 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400849 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800850 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400851
Romain Guy5c16f3e2010-01-12 17:24:58 -0800852 // Make sure the live folder exists
853 final ProviderInfo providerInfo =
854 context.getPackageManager().resolveContentProvider(
855 uri.getAuthority(), 0);
856
857 if (providerInfo == null && !isSafeMode) {
858 itemsToRemove.add(id);
859 } else {
860 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
861
862 intentDescription = c.getString(intentIndex);
863 intent = null;
864 if (intentDescription != null) {
865 try {
866 intent = Intent.parseUri(intentDescription, 0);
867 } catch (URISyntaxException e) {
868 // Ignore, a live folder might not have a base intent
869 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400870 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800871
872 liveFolderInfo.title = c.getString(titleIndex);
873 liveFolderInfo.id = id;
874 liveFolderInfo.uri = uri;
875 container = c.getInt(containerIndex);
876 liveFolderInfo.container = container;
877 liveFolderInfo.screen = c.getInt(screenIndex);
878 liveFolderInfo.cellX = c.getInt(cellXIndex);
879 liveFolderInfo.cellY = c.getInt(cellYIndex);
880 liveFolderInfo.baseIntent = intent;
881 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
Daniel Sandler8802e962010-05-26 16:28:16 -0400882
883 // check & update map of what's occupied
884 if (!checkItemPlacement(occupied, liveFolderInfo)) {
885 break;
886 }
887
Romain Guy5c16f3e2010-01-12 17:24:58 -0800888 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
889 iconResourceIndex, liveFolderInfo);
890
891 switch (container) {
892 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
893 mItems.add(liveFolderInfo);
894 break;
895 }
896 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400897 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400898 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500899
Joe Onorato9c1289c2009-08-17 11:03:03 -0400900 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
901 // Read all Launcher-specific widget details
902 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800903 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400904
Romain Guy629de3e2010-01-13 12:20:59 -0800905 final AppWidgetProviderInfo provider =
906 widgets.getAppWidgetInfo(appWidgetId);
907
908 if (!isSafeMode && (provider == null || provider.provider == null ||
909 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700910 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
911 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800912 itemsToRemove.add(id);
913 } else {
914 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
915 appWidgetInfo.id = id;
916 appWidgetInfo.screen = c.getInt(screenIndex);
917 appWidgetInfo.cellX = c.getInt(cellXIndex);
918 appWidgetInfo.cellY = c.getInt(cellYIndex);
919 appWidgetInfo.spanX = c.getInt(spanXIndex);
920 appWidgetInfo.spanY = c.getInt(spanYIndex);
921
922 container = c.getInt(containerIndex);
923 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
924 Log.e(TAG, "Widget found where container "
925 + "!= CONTAINER_DESKTOP -- ignoring!");
926 continue;
927 }
928 appWidgetInfo.container = c.getInt(containerIndex);
929
Daniel Sandler8802e962010-05-26 16:28:16 -0400930 // check & update map of what's occupied
931 if (!checkItemPlacement(occupied, appWidgetInfo)) {
932 break;
933 }
934
Romain Guy629de3e2010-01-13 12:20:59 -0800935 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400936 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400937 break;
938 }
939 } catch (Exception e) {
940 Log.w(TAG, "Desktop items loading interrupted:", e);
941 }
942 }
943 } finally {
944 c.close();
945 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800946
947 if (itemsToRemove.size() > 0) {
948 ContentProviderClient client = contentResolver.acquireContentProviderClient(
949 LauncherSettings.Favorites.CONTENT_URI);
950 // Remove dead items
951 for (long id : itemsToRemove) {
952 if (DEBUG_LOADERS) {
953 Log.d(TAG, "Removed id = " + id);
954 }
955 // Don't notify content observers
956 try {
957 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
958 null, null);
959 } catch (RemoteException e) {
960 Log.w(TAG, "Could not remove id = " + id);
961 }
962 }
963 }
964
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800965 if (DEBUG_LOADERS) {
966 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Daniel Sandler8802e962010-05-26 16:28:16 -0400967 Log.d(TAG, "workspace layout: ");
968 for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
969 String line = "";
970 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
971 if (s > 0) {
972 line += " | ";
973 }
974 for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
975 line += ((occupied[s][x][y] != null) ? "#" : ".");
976 }
977 }
978 Log.d(TAG, "[ " + line + " ]");
979 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800980 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400981 }
982
983 /**
984 * Read everything out of our database.
985 */
986 private void bindWorkspace() {
987 final long t = SystemClock.uptimeMillis();
988
989 // Don't use these two variables in any of the callback runnables.
990 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800991 final Callbacks oldCallbacks = mCallbacks.get();
992 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400993 // This launcher has exited and nobody bothered to tell us. Just bail.
994 Log.w(TAG, "LoaderThread running with no launcher");
995 return;
996 }
997
998 int N;
999 // Tell the workspace that we're about to start firing items at it
1000 mHandler.post(new Runnable() {
1001 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001002 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001003 if (callbacks != null) {
1004 callbacks.startBinding();
1005 }
1006 }
1007 });
1008 // Add the items to the workspace.
1009 N = mItems.size();
1010 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1011 final int start = i;
1012 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
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.bindItems(mItems, start, start+chunkSize);
1018 }
1019 }
1020 });
1021 }
Joe Onoratoad72e172009-11-06 16:25:04 -05001022 mHandler.post(new Runnable() {
1023 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001024 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -05001025 if (callbacks != null) {
1026 callbacks.bindFolders(mFolders);
1027 }
1028 }
1029 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001030 // Wait until the queue goes empty.
1031 mHandler.postIdle(new Runnable() {
1032 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001033 if (DEBUG_LOADERS) {
1034 Log.d(TAG, "Going to start binding widgets soon.");
1035 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001036 }
1037 });
1038 // Bind the widgets, one at a time.
1039 // WARNING: this is calling into the workspace from the background thread,
1040 // but since getCurrentScreen() just returns the int, we should be okay. This
1041 // is just a hint for the order, and if it's wrong, we'll be okay.
1042 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -08001043 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001044 N = mAppWidgets.size();
1045 // once for the current screen
1046 for (int i=0; i<N; i++) {
1047 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1048 if (widget.screen == currentScreen) {
1049 mHandler.post(new Runnable() {
1050 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001051 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001052 if (callbacks != null) {
1053 callbacks.bindAppWidget(widget);
1054 }
1055 }
1056 });
1057 }
1058 }
1059 // once for the other screens
1060 for (int i=0; i<N; i++) {
1061 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1062 if (widget.screen != currentScreen) {
1063 mHandler.post(new Runnable() {
1064 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001065 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001066 if (callbacks != null) {
1067 callbacks.bindAppWidget(widget);
1068 }
1069 }
1070 });
1071 }
1072 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001073 // Tell the workspace that we're done.
1074 mHandler.post(new Runnable() {
1075 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001076 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001077 if (callbacks != null) {
1078 callbacks.finishBindingItems();
1079 }
1080 }
1081 });
1082 // If we're profiling, this is the last thing in the queue.
1083 mHandler.post(new Runnable() {
1084 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001085 if (DEBUG_LOADERS) {
1086 Log.d(TAG, "bound workspace in "
1087 + (SystemClock.uptimeMillis()-t) + "ms");
1088 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001089 if (Launcher.PROFILE_ROTATE) {
1090 android.os.Debug.stopMethodTracing();
1091 }
1092 }
1093 });
1094 }
1095
Daniel Sandlerdca66122010-04-13 16:23:58 -04001096 private void loadAndBindAllApps() {
1097 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1098
Joe Onoratod65d08e2010-04-20 15:43:37 -04001099 // Don't use these two variables in any of the callback runnables.
1100 // Otherwise we hold a reference to them.
1101 final Callbacks oldCallbacks = mCallbacks.get();
1102 if (oldCallbacks == null) {
1103 // This launcher has exited and nobody bothered to tell us. Just bail.
1104 Log.w(TAG, "LoaderThread running with no launcher (loadAndBindAllApps)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001105 return;
1106 }
1107
Joe Onoratod65d08e2010-04-20 15:43:37 -04001108 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1109 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1110
Joe Onorato0589f0f2010-02-08 13:44:00 -08001111 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001112 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001113
Joe Onoratod65d08e2010-04-20 15:43:37 -04001114 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001115
Joe Onoratod65d08e2010-04-20 15:43:37 -04001116 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001117 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001118 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001119 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001120 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001121 if (i == 0) {
1122 // This needs to happen inside the same lock block as when we
1123 // prepare the first batch for bindAllApplications. Otherwise
1124 // the package changed receiver can come in and double-add
1125 // (or miss one?).
1126 mAllAppsList.clear();
1127 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1128 apps = packageManager.queryIntentActivities(mainIntent, 0);
1129 if (DEBUG_LOADERS) {
1130 Log.d(TAG, "queryIntentActivities took "
1131 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1132 }
1133 if (apps == null) {
1134 return;
1135 }
1136 N = apps.size();
1137 if (DEBUG_LOADERS) {
1138 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1139 }
1140 if (N == 0) {
1141 // There are no apps?!?
1142 return;
1143 }
1144 if (mBatchSize == 0) {
1145 batchSize = N;
1146 } else {
1147 batchSize = mBatchSize;
1148 }
1149
1150 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1151 Collections.sort(apps,
1152 new ResolveInfo.DisplayNameComparator(packageManager));
1153 if (DEBUG_LOADERS) {
1154 Log.d(TAG, "sort took "
1155 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1156 }
1157 }
1158
Daniel Sandlerdca66122010-04-13 16:23:58 -04001159 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1160
Joe Onoratod65d08e2010-04-20 15:43:37 -04001161 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001162 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001163 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001164 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001165 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001166 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001167
1168 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001169 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001170 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1171 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1172
1173 mHandler.post(new Runnable() {
1174 public void run() {
1175 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001176 if (callbacks != null) {
1177 if (first) {
1178 mBeforeFirstLoad = false;
1179 callbacks.bindAllApplications(added);
1180 } else {
1181 callbacks.bindAppsAdded(added);
1182 }
1183 if (DEBUG_LOADERS) {
1184 Log.d(TAG, "bound " + added.size() + " apps in "
1185 + (SystemClock.uptimeMillis() - t) + "ms");
1186 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001187 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001188 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001189 }
1190 }
1191 });
1192
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001193 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001194 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001195 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001196 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001197 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001198
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001199 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001200 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001201 if (DEBUG_LOADERS) {
1202 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1203 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001204 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001205 } catch (InterruptedException exc) { }
1206 }
1207 }
1208
1209 if (DEBUG_LOADERS) {
1210 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001211 + (SystemClock.uptimeMillis()-t) + "ms"
1212 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001213 }
1214 }
1215
Joe Onoratobe386092009-11-17 17:32:16 -08001216 public void dumpState() {
1217 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1218 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1219 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1220 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1221 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1222 }
1223 }
1224
1225 public void dumpState() {
1226 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1227 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1228 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1229 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1230 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1231 if (mLoaderThread != null) {
1232 mLoaderThread.dumpState();
1233 } else {
1234 Log.d(TAG, "mLoader.mLoaderThread=null");
1235 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001236 }
1237 }
1238
1239 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001240 * This is called from the code that adds shortcuts from the intent receiver. This
1241 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001242 */
Joe Onorato56d82912010-03-07 14:32:10 -05001243 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001244 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001245 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001246
Joe Onorato56d82912010-03-07 14:32:10 -05001247 /**
1248 * Make an ShortcutInfo object for a shortcut that is an application.
1249 *
1250 * If c is not null, then it will be used to fill in missing data like the title and icon.
1251 */
1252 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1253 Cursor c, int iconIndex, int titleIndex) {
1254 Bitmap icon = null;
1255 final ShortcutInfo info = new ShortcutInfo();
1256
1257 ComponentName componentName = intent.getComponent();
1258 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001259 return null;
1260 }
1261
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001262 // TODO: See if the PackageManager knows about this case. If it doesn't
1263 // then return null & delete this.
1264
Joe Onorato56d82912010-03-07 14:32:10 -05001265 // the resource -- This may implicitly give us back the fallback icon,
1266 // but don't worry about that. All we're doing with usingFallbackIcon is
1267 // to avoid saving lots of copies of that in the database, and most apps
1268 // have icons anyway.
1269 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1270 if (resolveInfo != null) {
1271 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001272 }
Joe Onorato56d82912010-03-07 14:32:10 -05001273 // the db
1274 if (icon == null) {
1275 if (c != null) {
1276 icon = getIconFromCursor(c, iconIndex);
1277 }
1278 }
1279 // the fallback icon
1280 if (icon == null) {
1281 icon = getFallbackIcon();
1282 info.usingFallbackIcon = true;
1283 }
1284 info.setIcon(icon);
1285
1286 // from the resource
1287 if (resolveInfo != null) {
1288 info.title = resolveInfo.activityInfo.loadLabel(manager);
1289 }
1290 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001291 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001292 if (c != null) {
1293 info.title = c.getString(titleIndex);
1294 }
1295 }
1296 // fall back to the class name of the activity
1297 if (info.title == null) {
1298 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001299 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001300 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1301 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001302 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001303
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001304 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001305 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001306 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001307 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001308 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1309 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001310
Joe Onorato56d82912010-03-07 14:32:10 -05001311 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001312 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001313 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001314
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001315 // TODO: If there's an explicit component and we can't install that, delete it.
1316
Joe Onorato56d82912010-03-07 14:32:10 -05001317 info.title = c.getString(titleIndex);
1318
Joe Onorato9c1289c2009-08-17 11:03:03 -04001319 int iconType = c.getInt(iconTypeIndex);
1320 switch (iconType) {
1321 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1322 String packageName = c.getString(iconPackageIndex);
1323 String resourceName = c.getString(iconResourceIndex);
1324 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001325 info.customIcon = false;
1326 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001327 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001328 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001329 if (resources != null) {
1330 final int id = resources.getIdentifier(resourceName, null, null);
1331 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1332 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001333 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001334 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001335 }
Joe Onorato56d82912010-03-07 14:32:10 -05001336 // the db
1337 if (icon == null) {
1338 icon = getIconFromCursor(c, iconIndex);
1339 }
1340 // the fallback icon
1341 if (icon == null) {
1342 icon = getFallbackIcon();
1343 info.usingFallbackIcon = true;
1344 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001345 break;
1346 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001347 icon = getIconFromCursor(c, iconIndex);
1348 if (icon == null) {
1349 icon = getFallbackIcon();
1350 info.customIcon = false;
1351 info.usingFallbackIcon = true;
1352 } else {
1353 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001354 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001355 break;
1356 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001357 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001358 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001359 info.customIcon = false;
1360 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001361 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001362 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001363 return info;
1364 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001365
Joe Onorato56d82912010-03-07 14:32:10 -05001366 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1367 if (false) {
1368 Log.d(TAG, "getIconFromCursor app="
1369 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1370 }
1371 byte[] data = c.getBlob(iconIndex);
1372 try {
1373 return BitmapFactory.decodeByteArray(data, 0, data.length);
1374 } catch (Exception e) {
1375 return null;
1376 }
1377 }
1378
Joe Onorato0589f0f2010-02-08 13:44:00 -08001379 ShortcutInfo addShortcut(Context context, Intent data,
1380 CellLayout.CellInfo cellInfo, boolean notify) {
1381
1382 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1383 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1384 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1385
1386 return info;
1387 }
1388
1389 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1390 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1391 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1392 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1393
1394 Bitmap icon = null;
1395 boolean filtered = false;
1396 boolean customIcon = false;
1397 ShortcutIconResource iconResource = null;
1398
1399 if (bitmap != null && bitmap instanceof Bitmap) {
1400 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1401 filtered = true;
1402 customIcon = true;
1403 } else {
1404 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1405 if (extra != null && extra instanceof ShortcutIconResource) {
1406 try {
1407 iconResource = (ShortcutIconResource) extra;
1408 final PackageManager packageManager = context.getPackageManager();
1409 Resources resources = packageManager.getResourcesForApplication(
1410 iconResource.packageName);
1411 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1412 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1413 } catch (Exception e) {
1414 Log.w(TAG, "Could not load shortcut icon: " + extra);
1415 }
1416 }
1417 }
1418
Joe Onorato0589f0f2010-02-08 13:44:00 -08001419 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001420
1421 if (icon == null) {
1422 icon = getFallbackIcon();
1423 info.usingFallbackIcon = true;
1424 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001425 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001426
Joe Onorato0589f0f2010-02-08 13:44:00 -08001427 info.title = name;
1428 info.intent = intent;
1429 info.customIcon = customIcon;
1430 info.iconResource = iconResource;
1431
1432 return info;
1433 }
1434
Joe Onorato9c1289c2009-08-17 11:03:03 -04001435 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1436 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1437
1438 int iconType = c.getInt(iconTypeIndex);
1439 switch (iconType) {
1440 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1441 String packageName = c.getString(iconPackageIndex);
1442 String resourceName = c.getString(iconResourceIndex);
1443 PackageManager packageManager = context.getPackageManager();
1444 try {
1445 Resources resources = packageManager.getResourcesForApplication(packageName);
1446 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001447 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1448 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001449 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001450 liveFolderInfo.icon = Utilities.createIconBitmap(
1451 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1452 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001453 }
1454 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1455 liveFolderInfo.iconResource.packageName = packageName;
1456 liveFolderInfo.iconResource.resourceName = resourceName;
1457 break;
1458 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001459 liveFolderInfo.icon = Utilities.createIconBitmap(
1460 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1461 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001462 }
1463 }
1464
Joe Onorato56d82912010-03-07 14:32:10 -05001465 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1466 // If this icon doesn't have a custom icon, check to see
1467 // what's stored in the DB, and if it doesn't match what
1468 // we're going to show, store what we are going to show back
1469 // into the DB. We do this so when we're loading, if the
1470 // package manager can't find an icon (for example because
1471 // the app is on SD) then we can use that instead.
1472 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1473 boolean needSave;
1474 byte[] data = c.getBlob(iconIndex);
1475 try {
1476 if (data != null) {
1477 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1478 Bitmap loaded = info.getIcon(mIconCache);
1479 needSave = !saved.sameAs(loaded);
1480 } else {
1481 needSave = true;
1482 }
1483 } catch (Exception e) {
1484 needSave = true;
1485 }
1486 if (needSave) {
1487 Log.d(TAG, "going to save icon bitmap for info=" + info);
1488 // This is slower than is ideal, but this only happens either
1489 // after the froyo OTA or when the app is updated with a new
1490 // icon.
1491 updateItemInDatabase(context, info);
1492 }
1493 }
1494 }
1495
Joe Onorato9c1289c2009-08-17 11:03:03 -04001496 /**
1497 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1498 * or make a new one.
1499 */
1500 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1501 // See if a placeholder was created for us already
1502 FolderInfo folderInfo = folders.get(id);
1503 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1504 // No placeholder -- create a new instance
1505 folderInfo = new UserFolderInfo();
1506 folders.put(id, folderInfo);
1507 }
1508 return (UserFolderInfo) folderInfo;
1509 }
1510
1511 /**
1512 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1513 * new one.
1514 */
1515 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1516 // See if a placeholder was created for us already
1517 FolderInfo folderInfo = folders.get(id);
1518 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1519 // No placeholder -- create a new instance
1520 folderInfo = new LiveFolderInfo();
1521 folders.put(id, folderInfo);
1522 }
1523 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001524 }
1525
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001526 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1527 String label = activityInfo.loadLabel(manager).toString();
1528 if (label == null) {
1529 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1530 if (label == null) {
1531 label = activityInfo.name;
1532 }
1533 }
1534 return label;
1535 }
1536
Joe Onorato9c1289c2009-08-17 11:03:03 -04001537 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001538 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001539 = new Comparator<ApplicationInfo>() {
1540 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1541 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001542 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001543 };
Joe Onoratobe386092009-11-17 17:32:16 -08001544
1545 public void dumpState() {
1546 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1547 Log.d(TAG, "mCallbacks=" + mCallbacks);
1548 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1549 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1550 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1551 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1552 mLoader.dumpState();
1553 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001554}