blob: 26671ad2ce13868dba05673303a8f59c1ffba65a [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) {
675 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
676 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
677 if (occupied[item.screen][x][y] != null) {
678 Log.e(TAG, "Error loading shortcut " + item
679 + " into cell (" + item.screen + ":"
680 + x + "," + y
681 + ") occupied by "
682 + occupied[item.screen][x][y]);
683 return false;
684 }
685 }
686 }
687 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
688 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
689 occupied[item.screen][x][y] = item;
690 }
691 }
692 return true;
693 }
694
Joe Onorato9c1289c2009-08-17 11:03:03 -0400695 private void loadWorkspace() {
696 long t = SystemClock.uptimeMillis();
697
698 final Context context = mContext;
699 final ContentResolver contentResolver = context.getContentResolver();
700 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800701 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800702 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400703
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400704 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500705 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800706 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400707
Romain Guy5c16f3e2010-01-12 17:24:58 -0800708 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
709
Joe Onorato9c1289c2009-08-17 11:03:03 -0400710 final Cursor c = contentResolver.query(
711 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
712
Daniel Sandler8802e962010-05-26 16:28:16 -0400713 final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
714
Joe Onorato9c1289c2009-08-17 11:03:03 -0400715 try {
716 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
717 final int intentIndex = c.getColumnIndexOrThrow
718 (LauncherSettings.Favorites.INTENT);
719 final int titleIndex = c.getColumnIndexOrThrow
720 (LauncherSettings.Favorites.TITLE);
721 final int iconTypeIndex = c.getColumnIndexOrThrow(
722 LauncherSettings.Favorites.ICON_TYPE);
723 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
724 final int iconPackageIndex = c.getColumnIndexOrThrow(
725 LauncherSettings.Favorites.ICON_PACKAGE);
726 final int iconResourceIndex = c.getColumnIndexOrThrow(
727 LauncherSettings.Favorites.ICON_RESOURCE);
728 final int containerIndex = c.getColumnIndexOrThrow(
729 LauncherSettings.Favorites.CONTAINER);
730 final int itemTypeIndex = c.getColumnIndexOrThrow(
731 LauncherSettings.Favorites.ITEM_TYPE);
732 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
733 LauncherSettings.Favorites.APPWIDGET_ID);
734 final int screenIndex = c.getColumnIndexOrThrow(
735 LauncherSettings.Favorites.SCREEN);
736 final int cellXIndex = c.getColumnIndexOrThrow
737 (LauncherSettings.Favorites.CELLX);
738 final int cellYIndex = c.getColumnIndexOrThrow
739 (LauncherSettings.Favorites.CELLY);
740 final int spanXIndex = c.getColumnIndexOrThrow
741 (LauncherSettings.Favorites.SPANX);
742 final int spanYIndex = c.getColumnIndexOrThrow(
743 LauncherSettings.Favorites.SPANY);
744 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
745 final int displayModeIndex = c.getColumnIndexOrThrow(
746 LauncherSettings.Favorites.DISPLAY_MODE);
747
Joe Onorato0589f0f2010-02-08 13:44:00 -0800748 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400749 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400750 LauncherAppWidgetInfo appWidgetInfo;
751 int container;
752 long id;
753 Intent intent;
754
755 while (!mStopped && c.moveToNext()) {
756 try {
757 int itemType = c.getInt(itemTypeIndex);
758
759 switch (itemType) {
760 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
761 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
762 intentDescription = c.getString(intentIndex);
763 try {
764 intent = Intent.parseUri(intentDescription, 0);
765 } catch (URISyntaxException e) {
766 continue;
767 }
768
769 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500770 info = getShortcutInfo(manager, intent, context, c, iconIndex,
771 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400772 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800773 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500774 iconPackageIndex, iconResourceIndex, iconIndex,
775 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400776 }
777
778 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500779 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400780
Joe Onorato56d82912010-03-07 14:32:10 -0500781 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400782 info.id = c.getLong(idIndex);
783 container = c.getInt(containerIndex);
784 info.container = container;
785 info.screen = c.getInt(screenIndex);
786 info.cellX = c.getInt(cellXIndex);
787 info.cellY = c.getInt(cellYIndex);
788
Daniel Sandler8802e962010-05-26 16:28:16 -0400789 // check & update map of what's occupied
790 if (!checkItemPlacement(occupied, info)) {
791 break;
792 }
793
Joe Onorato9c1289c2009-08-17 11:03:03 -0400794 switch (container) {
795 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
796 mItems.add(info);
797 break;
798 default:
799 // Item is in a user folder
800 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500801 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400802 folderInfo.add(info);
803 break;
804 }
Joe Onorato56d82912010-03-07 14:32:10 -0500805 } else {
806 // Failed to load the shortcut, probably because the
807 // activity manager couldn't resolve it (maybe the app
808 // was uninstalled), or the db row was somehow screwed up.
809 // Delete it.
810 id = c.getLong(idIndex);
811 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
812 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
813 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400814 }
815 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400816
Joe Onoratoad72e172009-11-06 16:25:04 -0500817 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400818 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500819 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400820
821 folderInfo.title = c.getString(titleIndex);
822
823 folderInfo.id = id;
824 container = c.getInt(containerIndex);
825 folderInfo.container = container;
826 folderInfo.screen = c.getInt(screenIndex);
827 folderInfo.cellX = c.getInt(cellXIndex);
828 folderInfo.cellY = c.getInt(cellYIndex);
829
Daniel Sandler8802e962010-05-26 16:28:16 -0400830 // check & update map of what's occupied
831 if (!checkItemPlacement(occupied, folderInfo)) {
832 break;
833 }
834
Joe Onorato9c1289c2009-08-17 11:03:03 -0400835 switch (container) {
836 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
837 mItems.add(folderInfo);
838 break;
839 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500840
841 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400842 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500843
Joe Onorato9c1289c2009-08-17 11:03:03 -0400844 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400845 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800846 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400847
Romain Guy5c16f3e2010-01-12 17:24:58 -0800848 // Make sure the live folder exists
849 final ProviderInfo providerInfo =
850 context.getPackageManager().resolveContentProvider(
851 uri.getAuthority(), 0);
852
853 if (providerInfo == null && !isSafeMode) {
854 itemsToRemove.add(id);
855 } else {
856 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
857
858 intentDescription = c.getString(intentIndex);
859 intent = null;
860 if (intentDescription != null) {
861 try {
862 intent = Intent.parseUri(intentDescription, 0);
863 } catch (URISyntaxException e) {
864 // Ignore, a live folder might not have a base intent
865 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400866 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800867
868 liveFolderInfo.title = c.getString(titleIndex);
869 liveFolderInfo.id = id;
870 liveFolderInfo.uri = uri;
871 container = c.getInt(containerIndex);
872 liveFolderInfo.container = container;
873 liveFolderInfo.screen = c.getInt(screenIndex);
874 liveFolderInfo.cellX = c.getInt(cellXIndex);
875 liveFolderInfo.cellY = c.getInt(cellYIndex);
876 liveFolderInfo.baseIntent = intent;
877 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
Daniel Sandler8802e962010-05-26 16:28:16 -0400878
879 // check & update map of what's occupied
880 if (!checkItemPlacement(occupied, liveFolderInfo)) {
881 break;
882 }
883
Romain Guy5c16f3e2010-01-12 17:24:58 -0800884 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
885 iconResourceIndex, liveFolderInfo);
886
887 switch (container) {
888 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
889 mItems.add(liveFolderInfo);
890 break;
891 }
892 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400893 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400894 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500895
Joe Onorato9c1289c2009-08-17 11:03:03 -0400896 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
897 // Read all Launcher-specific widget details
898 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800899 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400900
Romain Guy629de3e2010-01-13 12:20:59 -0800901 final AppWidgetProviderInfo provider =
902 widgets.getAppWidgetInfo(appWidgetId);
903
904 if (!isSafeMode && (provider == null || provider.provider == null ||
905 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700906 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
907 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800908 itemsToRemove.add(id);
909 } else {
910 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
911 appWidgetInfo.id = id;
912 appWidgetInfo.screen = c.getInt(screenIndex);
913 appWidgetInfo.cellX = c.getInt(cellXIndex);
914 appWidgetInfo.cellY = c.getInt(cellYIndex);
915 appWidgetInfo.spanX = c.getInt(spanXIndex);
916 appWidgetInfo.spanY = c.getInt(spanYIndex);
917
918 container = c.getInt(containerIndex);
919 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
920 Log.e(TAG, "Widget found where container "
921 + "!= CONTAINER_DESKTOP -- ignoring!");
922 continue;
923 }
924 appWidgetInfo.container = c.getInt(containerIndex);
925
Daniel Sandler8802e962010-05-26 16:28:16 -0400926 // check & update map of what's occupied
927 if (!checkItemPlacement(occupied, appWidgetInfo)) {
928 break;
929 }
930
Romain Guy629de3e2010-01-13 12:20:59 -0800931 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400932 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400933 break;
934 }
935 } catch (Exception e) {
936 Log.w(TAG, "Desktop items loading interrupted:", e);
937 }
938 }
939 } finally {
940 c.close();
941 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800942
943 if (itemsToRemove.size() > 0) {
944 ContentProviderClient client = contentResolver.acquireContentProviderClient(
945 LauncherSettings.Favorites.CONTENT_URI);
946 // Remove dead items
947 for (long id : itemsToRemove) {
948 if (DEBUG_LOADERS) {
949 Log.d(TAG, "Removed id = " + id);
950 }
951 // Don't notify content observers
952 try {
953 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
954 null, null);
955 } catch (RemoteException e) {
956 Log.w(TAG, "Could not remove id = " + id);
957 }
958 }
959 }
960
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800961 if (DEBUG_LOADERS) {
962 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Daniel Sandler8802e962010-05-26 16:28:16 -0400963 Log.d(TAG, "workspace layout: ");
964 for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
965 String line = "";
966 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
967 if (s > 0) {
968 line += " | ";
969 }
970 for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
971 line += ((occupied[s][x][y] != null) ? "#" : ".");
972 }
973 }
974 Log.d(TAG, "[ " + line + " ]");
975 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800976 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400977 }
978
979 /**
980 * Read everything out of our database.
981 */
982 private void bindWorkspace() {
983 final long t = SystemClock.uptimeMillis();
984
985 // Don't use these two variables in any of the callback runnables.
986 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800987 final Callbacks oldCallbacks = mCallbacks.get();
988 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400989 // This launcher has exited and nobody bothered to tell us. Just bail.
990 Log.w(TAG, "LoaderThread running with no launcher");
991 return;
992 }
993
994 int N;
995 // Tell the workspace that we're about to start firing items at it
996 mHandler.post(new Runnable() {
997 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800998 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400999 if (callbacks != null) {
1000 callbacks.startBinding();
1001 }
1002 }
1003 });
1004 // Add the items to the workspace.
1005 N = mItems.size();
1006 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1007 final int start = i;
1008 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
1009 mHandler.post(new Runnable() {
1010 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001011 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012 if (callbacks != null) {
1013 callbacks.bindItems(mItems, start, start+chunkSize);
1014 }
1015 }
1016 });
1017 }
Joe Onoratoad72e172009-11-06 16:25:04 -05001018 mHandler.post(new Runnable() {
1019 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001020 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -05001021 if (callbacks != null) {
1022 callbacks.bindFolders(mFolders);
1023 }
1024 }
1025 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001026 // Wait until the queue goes empty.
1027 mHandler.postIdle(new Runnable() {
1028 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001029 if (DEBUG_LOADERS) {
1030 Log.d(TAG, "Going to start binding widgets soon.");
1031 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001032 }
1033 });
1034 // Bind the widgets, one at a time.
1035 // WARNING: this is calling into the workspace from the background thread,
1036 // but since getCurrentScreen() just returns the int, we should be okay. This
1037 // is just a hint for the order, and if it's wrong, we'll be okay.
1038 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -08001039 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001040 N = mAppWidgets.size();
1041 // once for the current screen
1042 for (int i=0; i<N; i++) {
1043 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1044 if (widget.screen == currentScreen) {
1045 mHandler.post(new Runnable() {
1046 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001047 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001048 if (callbacks != null) {
1049 callbacks.bindAppWidget(widget);
1050 }
1051 }
1052 });
1053 }
1054 }
1055 // once for the other screens
1056 for (int i=0; i<N; i++) {
1057 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1058 if (widget.screen != currentScreen) {
1059 mHandler.post(new Runnable() {
1060 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001061 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001062 if (callbacks != null) {
1063 callbacks.bindAppWidget(widget);
1064 }
1065 }
1066 });
1067 }
1068 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001069 // Tell the workspace that we're done.
1070 mHandler.post(new Runnable() {
1071 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001072 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001073 if (callbacks != null) {
1074 callbacks.finishBindingItems();
1075 }
1076 }
1077 });
1078 // If we're profiling, this is the last thing in the queue.
1079 mHandler.post(new Runnable() {
1080 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001081 if (DEBUG_LOADERS) {
1082 Log.d(TAG, "bound workspace in "
1083 + (SystemClock.uptimeMillis()-t) + "ms");
1084 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001085 if (Launcher.PROFILE_ROTATE) {
1086 android.os.Debug.stopMethodTracing();
1087 }
1088 }
1089 });
1090 }
1091
Daniel Sandlerdca66122010-04-13 16:23:58 -04001092 private void loadAndBindAllApps() {
1093 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1094
Joe Onoratod65d08e2010-04-20 15:43:37 -04001095 // Don't use these two variables in any of the callback runnables.
1096 // Otherwise we hold a reference to them.
1097 final Callbacks oldCallbacks = mCallbacks.get();
1098 if (oldCallbacks == null) {
1099 // This launcher has exited and nobody bothered to tell us. Just bail.
1100 Log.w(TAG, "LoaderThread running with no launcher (loadAndBindAllApps)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001101 return;
1102 }
1103
Joe Onoratod65d08e2010-04-20 15:43:37 -04001104 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1105 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1106
Joe Onorato0589f0f2010-02-08 13:44:00 -08001107 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001108 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001109
Joe Onoratod65d08e2010-04-20 15:43:37 -04001110 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001111
Joe Onoratod65d08e2010-04-20 15:43:37 -04001112 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001113 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001114 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001115 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001116 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001117 if (i == 0) {
1118 // This needs to happen inside the same lock block as when we
1119 // prepare the first batch for bindAllApplications. Otherwise
1120 // the package changed receiver can come in and double-add
1121 // (or miss one?).
1122 mAllAppsList.clear();
1123 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1124 apps = packageManager.queryIntentActivities(mainIntent, 0);
1125 if (DEBUG_LOADERS) {
1126 Log.d(TAG, "queryIntentActivities took "
1127 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1128 }
1129 if (apps == null) {
1130 return;
1131 }
1132 N = apps.size();
1133 if (DEBUG_LOADERS) {
1134 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1135 }
1136 if (N == 0) {
1137 // There are no apps?!?
1138 return;
1139 }
1140 if (mBatchSize == 0) {
1141 batchSize = N;
1142 } else {
1143 batchSize = mBatchSize;
1144 }
1145
1146 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1147 Collections.sort(apps,
1148 new ResolveInfo.DisplayNameComparator(packageManager));
1149 if (DEBUG_LOADERS) {
1150 Log.d(TAG, "sort took "
1151 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1152 }
1153 }
1154
Daniel Sandlerdca66122010-04-13 16:23:58 -04001155 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1156
Joe Onoratod65d08e2010-04-20 15:43:37 -04001157 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001158 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001159 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001160 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001161 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001162 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001163
1164 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001165 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001166 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1167 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1168
1169 mHandler.post(new Runnable() {
1170 public void run() {
1171 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001172 if (callbacks != null) {
1173 if (first) {
1174 mBeforeFirstLoad = false;
1175 callbacks.bindAllApplications(added);
1176 } else {
1177 callbacks.bindAppsAdded(added);
1178 }
1179 if (DEBUG_LOADERS) {
1180 Log.d(TAG, "bound " + added.size() + " apps in "
1181 + (SystemClock.uptimeMillis() - t) + "ms");
1182 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001183 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001184 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001185 }
1186 }
1187 });
1188
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001189 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001190 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001191 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001192 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001193 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001194
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001195 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001196 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001197 if (DEBUG_LOADERS) {
1198 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1199 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001200 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001201 } catch (InterruptedException exc) { }
1202 }
1203 }
1204
1205 if (DEBUG_LOADERS) {
1206 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001207 + (SystemClock.uptimeMillis()-t) + "ms"
1208 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001209 }
1210 }
1211
Joe Onoratobe386092009-11-17 17:32:16 -08001212 public void dumpState() {
1213 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1214 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1215 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1216 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
1217 Log.d(TAG, "mLoader.mLoaderThread.mWorkspaceDoneBinding=" + mWorkspaceDoneBinding);
1218 }
1219 }
1220
1221 public void dumpState() {
1222 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1223 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1224 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1225 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1226 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1227 if (mLoaderThread != null) {
1228 mLoaderThread.dumpState();
1229 } else {
1230 Log.d(TAG, "mLoader.mLoaderThread=null");
1231 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001232 }
1233 }
1234
1235 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001236 * This is called from the code that adds shortcuts from the intent receiver. This
1237 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001238 */
Joe Onorato56d82912010-03-07 14:32:10 -05001239 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001240 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001241 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001242
Joe Onorato56d82912010-03-07 14:32:10 -05001243 /**
1244 * Make an ShortcutInfo object for a shortcut that is an application.
1245 *
1246 * If c is not null, then it will be used to fill in missing data like the title and icon.
1247 */
1248 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1249 Cursor c, int iconIndex, int titleIndex) {
1250 Bitmap icon = null;
1251 final ShortcutInfo info = new ShortcutInfo();
1252
1253 ComponentName componentName = intent.getComponent();
1254 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001255 return null;
1256 }
1257
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001258 // TODO: See if the PackageManager knows about this case. If it doesn't
1259 // then return null & delete this.
1260
Joe Onorato56d82912010-03-07 14:32:10 -05001261 // the resource -- This may implicitly give us back the fallback icon,
1262 // but don't worry about that. All we're doing with usingFallbackIcon is
1263 // to avoid saving lots of copies of that in the database, and most apps
1264 // have icons anyway.
1265 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1266 if (resolveInfo != null) {
1267 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001268 }
Joe Onorato56d82912010-03-07 14:32:10 -05001269 // the db
1270 if (icon == null) {
1271 if (c != null) {
1272 icon = getIconFromCursor(c, iconIndex);
1273 }
1274 }
1275 // the fallback icon
1276 if (icon == null) {
1277 icon = getFallbackIcon();
1278 info.usingFallbackIcon = true;
1279 }
1280 info.setIcon(icon);
1281
1282 // from the resource
1283 if (resolveInfo != null) {
1284 info.title = resolveInfo.activityInfo.loadLabel(manager);
1285 }
1286 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001287 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001288 if (c != null) {
1289 info.title = c.getString(titleIndex);
1290 }
1291 }
1292 // fall back to the class name of the activity
1293 if (info.title == null) {
1294 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001295 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001296 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1297 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001298 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001299
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001300 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001301 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001302 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001303 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001304 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1305 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001306
Joe Onorato56d82912010-03-07 14:32:10 -05001307 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001308 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001309 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001310
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001311 // TODO: If there's an explicit component and we can't install that, delete it.
1312
Joe Onorato56d82912010-03-07 14:32:10 -05001313 info.title = c.getString(titleIndex);
1314
Joe Onorato9c1289c2009-08-17 11:03:03 -04001315 int iconType = c.getInt(iconTypeIndex);
1316 switch (iconType) {
1317 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1318 String packageName = c.getString(iconPackageIndex);
1319 String resourceName = c.getString(iconResourceIndex);
1320 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001321 info.customIcon = false;
1322 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001323 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001324 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001325 if (resources != null) {
1326 final int id = resources.getIdentifier(resourceName, null, null);
1327 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1328 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001329 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001330 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001331 }
Joe Onorato56d82912010-03-07 14:32:10 -05001332 // the db
1333 if (icon == null) {
1334 icon = getIconFromCursor(c, iconIndex);
1335 }
1336 // the fallback icon
1337 if (icon == null) {
1338 icon = getFallbackIcon();
1339 info.usingFallbackIcon = true;
1340 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001341 break;
1342 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001343 icon = getIconFromCursor(c, iconIndex);
1344 if (icon == null) {
1345 icon = getFallbackIcon();
1346 info.customIcon = false;
1347 info.usingFallbackIcon = true;
1348 } else {
1349 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001350 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001351 break;
1352 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001353 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001354 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001355 info.customIcon = false;
1356 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001357 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001358 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001359 return info;
1360 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001361
Joe Onorato56d82912010-03-07 14:32:10 -05001362 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1363 if (false) {
1364 Log.d(TAG, "getIconFromCursor app="
1365 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1366 }
1367 byte[] data = c.getBlob(iconIndex);
1368 try {
1369 return BitmapFactory.decodeByteArray(data, 0, data.length);
1370 } catch (Exception e) {
1371 return null;
1372 }
1373 }
1374
Joe Onorato0589f0f2010-02-08 13:44:00 -08001375 ShortcutInfo addShortcut(Context context, Intent data,
1376 CellLayout.CellInfo cellInfo, boolean notify) {
1377
1378 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1379 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1380 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1381
1382 return info;
1383 }
1384
1385 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1386 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1387 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1388 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1389
1390 Bitmap icon = null;
1391 boolean filtered = false;
1392 boolean customIcon = false;
1393 ShortcutIconResource iconResource = null;
1394
1395 if (bitmap != null && bitmap instanceof Bitmap) {
1396 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1397 filtered = true;
1398 customIcon = true;
1399 } else {
1400 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1401 if (extra != null && extra instanceof ShortcutIconResource) {
1402 try {
1403 iconResource = (ShortcutIconResource) extra;
1404 final PackageManager packageManager = context.getPackageManager();
1405 Resources resources = packageManager.getResourcesForApplication(
1406 iconResource.packageName);
1407 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1408 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1409 } catch (Exception e) {
1410 Log.w(TAG, "Could not load shortcut icon: " + extra);
1411 }
1412 }
1413 }
1414
Joe Onorato0589f0f2010-02-08 13:44:00 -08001415 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001416
1417 if (icon == null) {
1418 icon = getFallbackIcon();
1419 info.usingFallbackIcon = true;
1420 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001421 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001422
Joe Onorato0589f0f2010-02-08 13:44:00 -08001423 info.title = name;
1424 info.intent = intent;
1425 info.customIcon = customIcon;
1426 info.iconResource = iconResource;
1427
1428 return info;
1429 }
1430
Joe Onorato9c1289c2009-08-17 11:03:03 -04001431 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1432 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1433
1434 int iconType = c.getInt(iconTypeIndex);
1435 switch (iconType) {
1436 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1437 String packageName = c.getString(iconPackageIndex);
1438 String resourceName = c.getString(iconResourceIndex);
1439 PackageManager packageManager = context.getPackageManager();
1440 try {
1441 Resources resources = packageManager.getResourcesForApplication(packageName);
1442 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001443 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1444 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001445 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001446 liveFolderInfo.icon = Utilities.createIconBitmap(
1447 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1448 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001449 }
1450 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1451 liveFolderInfo.iconResource.packageName = packageName;
1452 liveFolderInfo.iconResource.resourceName = resourceName;
1453 break;
1454 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001455 liveFolderInfo.icon = Utilities.createIconBitmap(
1456 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1457 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001458 }
1459 }
1460
Joe Onorato56d82912010-03-07 14:32:10 -05001461 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1462 // If this icon doesn't have a custom icon, check to see
1463 // what's stored in the DB, and if it doesn't match what
1464 // we're going to show, store what we are going to show back
1465 // into the DB. We do this so when we're loading, if the
1466 // package manager can't find an icon (for example because
1467 // the app is on SD) then we can use that instead.
1468 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1469 boolean needSave;
1470 byte[] data = c.getBlob(iconIndex);
1471 try {
1472 if (data != null) {
1473 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1474 Bitmap loaded = info.getIcon(mIconCache);
1475 needSave = !saved.sameAs(loaded);
1476 } else {
1477 needSave = true;
1478 }
1479 } catch (Exception e) {
1480 needSave = true;
1481 }
1482 if (needSave) {
1483 Log.d(TAG, "going to save icon bitmap for info=" + info);
1484 // This is slower than is ideal, but this only happens either
1485 // after the froyo OTA or when the app is updated with a new
1486 // icon.
1487 updateItemInDatabase(context, info);
1488 }
1489 }
1490 }
1491
Joe Onorato9c1289c2009-08-17 11:03:03 -04001492 /**
1493 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1494 * or make a new one.
1495 */
1496 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1497 // See if a placeholder was created for us already
1498 FolderInfo folderInfo = folders.get(id);
1499 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1500 // No placeholder -- create a new instance
1501 folderInfo = new UserFolderInfo();
1502 folders.put(id, folderInfo);
1503 }
1504 return (UserFolderInfo) folderInfo;
1505 }
1506
1507 /**
1508 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1509 * new one.
1510 */
1511 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1512 // See if a placeholder was created for us already
1513 FolderInfo folderInfo = folders.get(id);
1514 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1515 // No placeholder -- create a new instance
1516 folderInfo = new LiveFolderInfo();
1517 folders.put(id, folderInfo);
1518 }
1519 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001520 }
1521
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001522 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1523 String label = activityInfo.loadLabel(manager).toString();
1524 if (label == null) {
1525 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1526 if (label == null) {
1527 label = activityInfo.name;
1528 }
1529 }
1530 return label;
1531 }
1532
Joe Onorato9c1289c2009-08-17 11:03:03 -04001533 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001534 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001535 = new Comparator<ApplicationInfo>() {
1536 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1537 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001538 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001539 };
Joe Onoratobe386092009-11-17 17:32:16 -08001540
1541 public void dumpState() {
1542 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1543 Log.d(TAG, "mCallbacks=" + mCallbacks);
1544 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1545 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1546 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1547 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1548 mLoader.dumpState();
1549 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001550}