blob: 96ceb747fdf0fbaf1da5dfdd3807a9205a34ab15 [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;
Daniel Sandler843e8602010-06-07 14:59:01 -040063 static final boolean PROFILE_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040064 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070065
Joe Onoratod65d08e2010-04-20 15:43:37 -040066 private int mBatchSize; // 0 is all apps at once
Daniel Sandler2ff10b32010-04-16 15:06:06 -040067 private int mAllAppsLoadDelay; // milliseconds between batches
Daniel Sandlerdca66122010-04-13 16:23:58 -040068
Joe Onoratof99f8c12009-10-31 17:27:36 -040069 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040070 private final Object mLock = new Object();
71 private DeferredHandler mHandler = new DeferredHandler();
72 private Loader mLoader = new Loader();
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073
Joe Onoratod65d08e2010-04-20 15:43:37 -040074 private boolean mBeforeFirstLoad = true; // only access this from main thread
Joe Onorato9c1289c2009-08-17 11:03:03 -040075 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080076
Joe Onoratofad1fb52010-05-04 12:12:41 -070077 private final Object mAllAppsListLock = new Object();
Joe Onorato0589f0f2010-02-08 13:44:00 -080078 private AllAppsList mAllAppsList;
79 private IconCache mIconCache;
80
81 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080082
Joe Onorato9c1289c2009-08-17 11:03:03 -040083 public interface Callbacks {
84 public int getCurrentWorkspaceScreen();
85 public void startBinding();
86 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050087 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040088 public void finishBindingItems();
89 public void bindAppWidget(LauncherAppWidgetInfo info);
90 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -050091 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
92 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
93 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
Daniel Sandler843e8602010-06-07 14:59:01 -040094 public boolean isAllAppsVisible();
Joe Onorato9c1289c2009-08-17 11:03:03 -040095 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080096
Joe Onorato0589f0f2010-02-08 13:44:00 -080097 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -040098 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -080099 mAllAppsList = new AllAppsList(iconCache);
100 mIconCache = iconCache;
101
102 mDefaultIcon = Utilities.createIconBitmap(
103 app.getPackageManager().getDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400104
105 mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
Joe Onoratod65d08e2010-04-20 15:43:37 -0400106
107 mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800108 }
109
Joe Onorato56d82912010-03-07 14:32:10 -0500110 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800111 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400112 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113
Joe Onorato9c1289c2009-08-17 11:03:03 -0400114 /**
115 * Adds an item to the DB if it was not created previously, or move it to a new
116 * <container, screen, cellX, cellY>
117 */
118 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
119 int screen, int cellX, int cellY) {
120 if (item.container == ItemInfo.NO_ID) {
121 // From all apps
122 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
123 } else {
124 // From somewhere else
125 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126 }
127 }
128
129 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400130 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700131 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400132 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
133 int cellX, int cellY) {
134 item.container = container;
135 item.screen = screen;
136 item.cellX = cellX;
137 item.cellY = cellY;
138
139 final ContentValues values = new ContentValues();
140 final ContentResolver cr = context.getContentResolver();
141
142 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
143 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
144 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
145 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
146
147 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700148 }
149
150 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400151 * Returns true if the shortcuts already exists in the database.
152 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400154 static boolean shortcutExists(Context context, String title, Intent intent) {
155 final ContentResolver cr = context.getContentResolver();
156 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
157 new String[] { "title", "intent" }, "title=? and intent=?",
158 new String[] { title, intent.toUri(0) }, null);
159 boolean result = false;
160 try {
161 result = c.moveToFirst();
162 } finally {
163 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800164 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400165 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700166 }
167
Joe Onorato9c1289c2009-08-17 11:03:03 -0400168 /**
169 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
170 */
171 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
172 final ContentResolver cr = context.getContentResolver();
173 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
174 "_id=? and (itemType=? or itemType=?)",
175 new String[] { String.valueOf(id),
176 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
177 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700178
Joe Onorato9c1289c2009-08-17 11:03:03 -0400179 try {
180 if (c.moveToFirst()) {
181 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
182 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
183 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
184 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
185 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
186 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800187
Joe Onorato9c1289c2009-08-17 11:03:03 -0400188 FolderInfo folderInfo = null;
189 switch (c.getInt(itemTypeIndex)) {
190 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
191 folderInfo = findOrMakeUserFolder(folderList, id);
192 break;
193 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
194 folderInfo = findOrMakeLiveFolder(folderList, id);
195 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700196 }
197
Joe Onorato9c1289c2009-08-17 11:03:03 -0400198 folderInfo.title = c.getString(titleIndex);
199 folderInfo.id = id;
200 folderInfo.container = c.getInt(containerIndex);
201 folderInfo.screen = c.getInt(screenIndex);
202 folderInfo.cellX = c.getInt(cellXIndex);
203 folderInfo.cellY = c.getInt(cellYIndex);
204
205 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700206 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400207 } finally {
208 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700209 }
210
211 return null;
212 }
213
Joe Onorato9c1289c2009-08-17 11:03:03 -0400214 /**
215 * Add an item to the database in a specified container. Sets the container, screen, cellX and
216 * cellY fields of the item. Also assigns an ID to the item.
217 */
218 static void addItemToDatabase(Context context, ItemInfo item, long container,
219 int screen, int cellX, int cellY, boolean notify) {
220 item.container = container;
221 item.screen = screen;
222 item.cellX = cellX;
223 item.cellY = cellY;
224
225 final ContentValues values = new ContentValues();
226 final ContentResolver cr = context.getContentResolver();
227
228 item.onAddToDatabase(values);
229
230 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
231 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
232
233 if (result != null) {
234 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700235 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700236 }
237
Joe Onorato9c1289c2009-08-17 11:03:03 -0400238 /**
239 * Update an item to the database in a specified container.
240 */
241 static void updateItemInDatabase(Context context, ItemInfo item) {
242 final ContentValues values = new ContentValues();
243 final ContentResolver cr = context.getContentResolver();
244
245 item.onAddToDatabase(values);
246
247 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
248 }
249
250 /**
251 * Removes the specified item from the database
252 * @param context
253 * @param item
254 */
255 static void deleteItemFromDatabase(Context context, ItemInfo item) {
256 final ContentResolver cr = context.getContentResolver();
257
258 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
259 }
260
261 /**
262 * Remove the contents of the specified folder from the database
263 */
264 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
265 final ContentResolver cr = context.getContentResolver();
266
267 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
268 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
269 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
270 }
271
272 /**
273 * Set this as the current Launcher activity object for the loader.
274 */
275 public void initialize(Callbacks callbacks) {
276 synchronized (mLock) {
277 mCallbacks = new WeakReference<Callbacks>(callbacks);
278 }
279 }
280
281 public void startLoader(Context context, boolean isLaunching) {
282 mLoader.startLoader(context, isLaunching);
283 }
284
285 public void stopLoader() {
286 mLoader.stopLoader();
287 }
288
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700289 /**
290 * We pick up most of the changes to all apps.
291 */
292 public void setAllAppsDirty() {
293 mLoader.setAllAppsDirty();
294 }
295
Joe Onorato9c1289c2009-08-17 11:03:03 -0400296 public void setWorkspaceDirty() {
297 mLoader.setWorkspaceDirty();
298 }
299
300 /**
301 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
302 * ACTION_PACKAGE_CHANGED.
303 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400304 public void onReceive(Context context, Intent intent) {
305 // Use the app as the context.
306 context = mApp;
307
Joe Onorato9c1289c2009-08-17 11:03:03 -0400308 ArrayList<ApplicationInfo> added = null;
309 ArrayList<ApplicationInfo> removed = null;
310 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400311
Joe Onoratofad1fb52010-05-04 12:12:41 -0700312 synchronized (mAllAppsListLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400313 if (mBeforeFirstLoad) {
314 // If we haven't even loaded yet, don't bother, since we'll just pick
315 // up the changes.
316 return;
317 }
318
Joe Onorato9c1289c2009-08-17 11:03:03 -0400319 final String action = intent.getAction();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400320
Joe Onorato64e6be72010-03-05 15:05:52 -0500321 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
322 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
323 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
324 final String packageName = intent.getData().getSchemeSpecificPart();
325 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400326
Joe Onorato64e6be72010-03-05 15:05:52 -0500327 if (packageName == null || packageName.length() == 0) {
328 // they sent us a bad intent
329 return;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400330 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500331
332 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400333 mAllAppsList.updatePackage(context, packageName);
Joe Onorato64e6be72010-03-05 15:05:52 -0500334 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
335 if (!replacing) {
336 mAllAppsList.removePackage(packageName);
337 }
338 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
339 // later, we will update the package at this time
340 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
341 if (!replacing) {
342 mAllAppsList.addPackage(context, packageName);
343 } else {
344 mAllAppsList.updatePackage(context, packageName);
345 }
346 }
Joe Onorato56d82912010-03-07 14:32:10 -0500347
348 if (mAllAppsList.added.size() > 0) {
349 added = mAllAppsList.added;
350 mAllAppsList.added = new ArrayList<ApplicationInfo>();
351 }
352 if (mAllAppsList.removed.size() > 0) {
353 removed = mAllAppsList.removed;
354 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
355 for (ApplicationInfo info: removed) {
356 mIconCache.remove(info.intent.getComponent());
357 }
358 }
359 if (mAllAppsList.modified.size() > 0) {
360 modified = mAllAppsList.modified;
361 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
362 }
363
364 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
365 if (callbacks == null) {
366 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
367 return;
368 }
369
370 if (added != null) {
371 final ArrayList<ApplicationInfo> addedFinal = added;
372 mHandler.post(new Runnable() {
373 public void run() {
374 callbacks.bindAppsAdded(addedFinal);
375 }
376 });
377 }
378 if (modified != null) {
379 final ArrayList<ApplicationInfo> modifiedFinal = modified;
380 mHandler.post(new Runnable() {
381 public void run() {
382 callbacks.bindAppsUpdated(modifiedFinal);
383 }
384 });
385 }
386 if (removed != null) {
387 final ArrayList<ApplicationInfo> removedFinal = removed;
388 mHandler.post(new Runnable() {
389 public void run() {
390 callbacks.bindAppsRemoved(removedFinal);
391 }
392 });
393 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500394 } else {
395 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
396 String packages[] = intent.getStringArrayExtra(
397 Intent.EXTRA_CHANGED_PACKAGE_LIST);
398 if (packages == null || packages.length == 0) {
399 return;
400 }
Joe Onorato56d82912010-03-07 14:32:10 -0500401 setAllAppsDirty();
402 setWorkspaceDirty();
403 startLoader(context, false);
Joe Onorato64e6be72010-03-05 15:05:52 -0500404 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
405 String packages[] = intent.getStringArrayExtra(
406 Intent.EXTRA_CHANGED_PACKAGE_LIST);
407 if (packages == null || packages.length == 0) {
408 return;
409 }
Joe Onorato56d82912010-03-07 14:32:10 -0500410 setAllAppsDirty();
411 setWorkspaceDirty();
412 startLoader(context, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400413 }
414 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400415 }
416 }
417
418 public class Loader {
419 private static final int ITEMS_CHUNK = 6;
420
421 private LoaderThread mLoaderThread;
422
423 private int mLastWorkspaceSeq = 0;
424 private int mWorkspaceSeq = 1;
425
426 private int mLastAllAppsSeq = 0;
427 private int mAllAppsSeq = 1;
428
Romain Guy84f296c2009-11-04 15:00:44 -0800429 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
430 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500431 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400432
433 /**
434 * Call this from the ui thread so the handler is initialized on the correct thread.
435 */
436 public Loader() {
437 }
438
439 public void startLoader(Context context, boolean isLaunching) {
440 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800441 if (DEBUG_LOADERS) {
442 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
443 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400444
Joe Onorato9c1289c2009-08-17 11:03:03 -0400445 // Don't bother to start the thread if we know it's not going to do anything
Joe Onoratoac033302010-04-13 17:19:18 -0700446 if (mCallbacks != null && mCallbacks.get() != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400447 LoaderThread oldThread = mLoaderThread;
448 if (oldThread != null) {
449 if (oldThread.isLaunching()) {
450 // don't downgrade isLaunching if we're already running
451 isLaunching = true;
452 }
453 oldThread.stopLocked();
454 }
455 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
456 mLoaderThread.start();
457 }
458 }
459 }
460
461 public void stopLoader() {
462 synchronized (mLock) {
463 if (mLoaderThread != null) {
464 mLoaderThread.stopLocked();
465 }
466 }
467 }
468
469 public void setWorkspaceDirty() {
470 synchronized (mLock) {
471 mWorkspaceSeq++;
472 }
473 }
474
475 public void setAllAppsDirty() {
476 synchronized (mLock) {
477 mAllAppsSeq++;
478 }
479 }
480
481 /**
482 * Runnable for the thread that loads the contents of the launcher:
483 * - workspace icons
484 * - widgets
485 * - all apps icons
486 */
487 private class LoaderThread extends Thread {
488 private Context mContext;
489 private Thread mWaitThread;
490 private boolean mIsLaunching;
491 private boolean mStopped;
Daniel Sandler843e8602010-06-07 14:59:01 -0400492 private boolean mLoadAndBindStepFinished;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400493
494 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
495 mContext = context;
496 mWaitThread = waitThread;
497 mIsLaunching = isLaunching;
498 }
499
500 boolean isLaunching() {
501 return mIsLaunching;
502 }
503
504 /**
505 * If another LoaderThread was supplied, we need to wait for that to finish before
506 * we start our processing. This keeps the ordering of the setting and clearing
507 * of the dirty flags correct by making sure we don't start processing stuff until
508 * they've had a chance to re-set them. We do this waiting the worker thread, not
509 * the ui thread to avoid ANRs.
510 */
511 private void waitForOtherThread() {
512 if (mWaitThread != null) {
513 boolean done = false;
514 while (!done) {
515 try {
516 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700517 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400518 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800519 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400520 }
521 }
522 mWaitThread = null;
523 }
524 }
525
Daniel Sandler843e8602010-06-07 14:59:01 -0400526 private void loadAndBindWorkspace() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400527 // Load the workspace only if it's dirty.
528 int workspaceSeq;
529 boolean workspaceDirty;
530 synchronized (mLock) {
531 workspaceSeq = mWorkspaceSeq;
532 workspaceDirty = mWorkspaceSeq != mLastWorkspaceSeq;
533 }
534 if (workspaceDirty) {
535 loadWorkspace();
536 }
537 synchronized (mLock) {
538 // If we're not stopped, and nobody has incremented mWorkspaceSeq.
539 if (mStopped) {
Daniel Sandler843e8602010-06-07 14:59:01 -0400540 if (PROFILE_LOADERS) {
541 android.os.Debug.stopMethodTracing();
542 }
543
Joe Onorato9c1289c2009-08-17 11:03:03 -0400544 return;
545 }
546 if (workspaceSeq == mWorkspaceSeq) {
547 mLastWorkspaceSeq = mWorkspaceSeq;
548 }
549 }
550
551 // Bind the workspace
552 bindWorkspace();
Daniel Sandler843e8602010-06-07 14:59:01 -0400553 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400554
Daniel Sandler843e8602010-06-07 14:59:01 -0400555 private void loadAndBindAllAppsIfDirty() {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400556 // Load all apps if they're dirty
557 int allAppsSeq;
558 boolean allAppsDirty;
559 synchronized (mLock) {
560 allAppsSeq = mAllAppsSeq;
561 allAppsDirty = mAllAppsSeq != mLastAllAppsSeq;
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800562 if (DEBUG_LOADERS) {
563 Log.d(TAG, "mAllAppsSeq=" + mAllAppsSeq
564 + " mLastAllAppsSeq=" + mLastAllAppsSeq + " allAppsDirty");
565 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400566 }
567 if (allAppsDirty) {
Daniel Sandlerdca66122010-04-13 16:23:58 -0400568 loadAndBindAllApps();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400569 }
570 synchronized (mLock) {
571 // If we're not stopped, and nobody has incremented mAllAppsSeq.
572 if (mStopped) {
573 return;
574 }
575 if (allAppsSeq == mAllAppsSeq) {
576 mLastAllAppsSeq = mAllAppsSeq;
577 }
578 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400579 }
580
581 private void waitForIdle() {
582 // Wait until the either we're stopped or the other threads are done.
583 // This way we don't start loading all apps until the workspace has settled
584 // down.
585 synchronized (LoaderThread.this) {
586 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
587
588 mHandler.postIdle(new Runnable() {
589 public void run() {
590 synchronized (LoaderThread.this) {
591 mLoadAndBindStepFinished = true;
592 if (DEBUG_LOADERS) {
593 Log.d(TAG, "done with previous binding step");
594 }
595 LoaderThread.this.notify();
596 }
597 }
598 });
599
600 while (!mStopped && !mLoadAndBindStepFinished) {
601 try {
602 this.wait();
603 } catch (InterruptedException ex) {
604 // Ignore
605 }
606 }
607 if (DEBUG_LOADERS) {
608 Log.d(TAG, "waited "
609 + (SystemClock.uptimeMillis()-workspaceWaitTime)
610 + "ms for previous step to finish binding");
611 }
612 }
613 }
614
615 public void run() {
616 waitForOtherThread();
617
618 // Optimize for end-user experience: if the Launcher is up and // running with the
619 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
620 // workspace first (default).
621 final Callbacks cbk = mCallbacks.get();
622 final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
623
624 // Elevate priority when Home launches for the first time to avoid
625 // starving at boot time. Staring at a blank home is not cool.
626 synchronized (mLock) {
627 android.os.Process.setThreadPriority(mIsLaunching
628 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
629 }
630
631 if (PROFILE_LOADERS) {
632 android.os.Debug.startMethodTracing("/sdcard/launcher-loaders");
633 }
634
635 if (loadWorkspaceFirst) {
636 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
637 loadAndBindWorkspace();
638 } else {
639 if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
640 loadAndBindAllAppsIfDirty();
641 }
642
643 // Whew! Hard work done.
644 synchronized (mLock) {
645 if (mIsLaunching) {
646 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
647 }
648 }
649
650 // second step
651 if (loadWorkspaceFirst) {
652 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
653 loadAndBindAllAppsIfDirty();
654 } else {
655 if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
656 loadAndBindWorkspace();
657 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400658
Joe Onorato9c1289c2009-08-17 11:03:03 -0400659 // Clear out this reference, otherwise we end up holding it until all of the
660 // callback runnables are done.
661 mContext = null;
662
663 synchronized (mLock) {
664 // Setting the reference is atomic, but we can't do it inside the other critical
665 // sections.
666 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400667 }
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700668
Daniel Sandler843e8602010-06-07 14:59:01 -0400669 if (PROFILE_LOADERS) {
670 android.os.Debug.stopMethodTracing();
671 }
672
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700673 // Trigger a gc to try to clean up after the stuff is done, since the
674 // renderscript allocations aren't charge to the java heap.
675 mHandler.post(new Runnable() {
676 public void run() {
677 System.gc();
678 }
679 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400680 }
681
682 public void stopLocked() {
683 synchronized (LoaderThread.this) {
684 mStopped = true;
685 this.notify();
686 }
687 }
688
689 /**
690 * Gets the callbacks object. If we've been stopped, or if the launcher object
Joe Onoratoc131b742010-03-11 15:45:05 -0800691 * has somehow been garbage collected, return null instead. Pass in the Callbacks
692 * object that was around when the deferred message was scheduled, and if there's
693 * a new Callbacks object around then also return null. This will save us from
694 * calling onto it with data that will be ignored.
Joe Onorato9c1289c2009-08-17 11:03:03 -0400695 */
Joe Onoratoc131b742010-03-11 15:45:05 -0800696 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400697 synchronized (mLock) {
698 if (mStopped) {
699 return null;
700 }
701
Joe Onoratoac033302010-04-13 17:19:18 -0700702 if (mCallbacks == null) {
703 return null;
704 }
705
Joe Onorato9c1289c2009-08-17 11:03:03 -0400706 final Callbacks callbacks = mCallbacks.get();
Joe Onoratoc131b742010-03-11 15:45:05 -0800707 if (callbacks != oldCallbacks) {
708 return null;
709 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400710 if (callbacks == null) {
711 Log.w(TAG, "no mCallbacks");
712 return null;
713 }
714
715 return callbacks;
716 }
717 }
718
Daniel Sandler8802e962010-05-26 16:28:16 -0400719 // check & update map of what's occupied; used to discard overlapping/invalid items
720 private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
721 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
722 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
723 if (occupied[item.screen][x][y] != null) {
724 Log.e(TAG, "Error loading shortcut " + item
725 + " into cell (" + item.screen + ":"
726 + x + "," + y
727 + ") occupied by "
728 + occupied[item.screen][x][y]);
729 return false;
730 }
731 }
732 }
733 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
734 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
735 occupied[item.screen][x][y] = item;
736 }
737 }
738 return true;
739 }
740
Joe Onorato9c1289c2009-08-17 11:03:03 -0400741 private void loadWorkspace() {
Daniel Sandler843e8602010-06-07 14:59:01 -0400742 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400743
744 final Context context = mContext;
745 final ContentResolver contentResolver = context.getContentResolver();
746 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800747 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800748 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400749
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400750 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500751 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800752 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400753
Romain Guy5c16f3e2010-01-12 17:24:58 -0800754 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
755
Joe Onorato9c1289c2009-08-17 11:03:03 -0400756 final Cursor c = contentResolver.query(
757 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
758
Daniel Sandler8802e962010-05-26 16:28:16 -0400759 final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
760
Joe Onorato9c1289c2009-08-17 11:03:03 -0400761 try {
762 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
763 final int intentIndex = c.getColumnIndexOrThrow
764 (LauncherSettings.Favorites.INTENT);
765 final int titleIndex = c.getColumnIndexOrThrow
766 (LauncherSettings.Favorites.TITLE);
767 final int iconTypeIndex = c.getColumnIndexOrThrow(
768 LauncherSettings.Favorites.ICON_TYPE);
769 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
770 final int iconPackageIndex = c.getColumnIndexOrThrow(
771 LauncherSettings.Favorites.ICON_PACKAGE);
772 final int iconResourceIndex = c.getColumnIndexOrThrow(
773 LauncherSettings.Favorites.ICON_RESOURCE);
774 final int containerIndex = c.getColumnIndexOrThrow(
775 LauncherSettings.Favorites.CONTAINER);
776 final int itemTypeIndex = c.getColumnIndexOrThrow(
777 LauncherSettings.Favorites.ITEM_TYPE);
778 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
779 LauncherSettings.Favorites.APPWIDGET_ID);
780 final int screenIndex = c.getColumnIndexOrThrow(
781 LauncherSettings.Favorites.SCREEN);
782 final int cellXIndex = c.getColumnIndexOrThrow
783 (LauncherSettings.Favorites.CELLX);
784 final int cellYIndex = c.getColumnIndexOrThrow
785 (LauncherSettings.Favorites.CELLY);
786 final int spanXIndex = c.getColumnIndexOrThrow
787 (LauncherSettings.Favorites.SPANX);
788 final int spanYIndex = c.getColumnIndexOrThrow(
789 LauncherSettings.Favorites.SPANY);
790 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
791 final int displayModeIndex = c.getColumnIndexOrThrow(
792 LauncherSettings.Favorites.DISPLAY_MODE);
793
Joe Onorato0589f0f2010-02-08 13:44:00 -0800794 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400795 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400796 LauncherAppWidgetInfo appWidgetInfo;
797 int container;
798 long id;
799 Intent intent;
800
801 while (!mStopped && c.moveToNext()) {
802 try {
803 int itemType = c.getInt(itemTypeIndex);
804
805 switch (itemType) {
806 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
807 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
808 intentDescription = c.getString(intentIndex);
809 try {
810 intent = Intent.parseUri(intentDescription, 0);
811 } catch (URISyntaxException e) {
812 continue;
813 }
814
815 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500816 info = getShortcutInfo(manager, intent, context, c, iconIndex,
817 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400818 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800819 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500820 iconPackageIndex, iconResourceIndex, iconIndex,
821 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400822 }
823
824 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500825 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400826
Joe Onorato56d82912010-03-07 14:32:10 -0500827 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400828 info.id = c.getLong(idIndex);
829 container = c.getInt(containerIndex);
830 info.container = container;
831 info.screen = c.getInt(screenIndex);
832 info.cellX = c.getInt(cellXIndex);
833 info.cellY = c.getInt(cellYIndex);
834
Daniel Sandler8802e962010-05-26 16:28:16 -0400835 // check & update map of what's occupied
836 if (!checkItemPlacement(occupied, info)) {
837 break;
838 }
839
Joe Onorato9c1289c2009-08-17 11:03:03 -0400840 switch (container) {
841 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
842 mItems.add(info);
843 break;
844 default:
845 // Item is in a user folder
846 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500847 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400848 folderInfo.add(info);
849 break;
850 }
Joe Onorato56d82912010-03-07 14:32:10 -0500851 } else {
852 // Failed to load the shortcut, probably because the
853 // activity manager couldn't resolve it (maybe the app
854 // was uninstalled), or the db row was somehow screwed up.
855 // Delete it.
856 id = c.getLong(idIndex);
857 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
858 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
859 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400860 }
861 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400862
Joe Onoratoad72e172009-11-06 16:25:04 -0500863 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400864 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500865 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400866
867 folderInfo.title = c.getString(titleIndex);
868
869 folderInfo.id = id;
870 container = c.getInt(containerIndex);
871 folderInfo.container = container;
872 folderInfo.screen = c.getInt(screenIndex);
873 folderInfo.cellX = c.getInt(cellXIndex);
874 folderInfo.cellY = c.getInt(cellYIndex);
875
Daniel Sandler8802e962010-05-26 16:28:16 -0400876 // check & update map of what's occupied
877 if (!checkItemPlacement(occupied, folderInfo)) {
878 break;
879 }
880
Joe Onorato9c1289c2009-08-17 11:03:03 -0400881 switch (container) {
882 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
883 mItems.add(folderInfo);
884 break;
885 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500886
887 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400888 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500889
Joe Onorato9c1289c2009-08-17 11:03:03 -0400890 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400891 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800892 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400893
Romain Guy5c16f3e2010-01-12 17:24:58 -0800894 // Make sure the live folder exists
895 final ProviderInfo providerInfo =
896 context.getPackageManager().resolveContentProvider(
897 uri.getAuthority(), 0);
898
899 if (providerInfo == null && !isSafeMode) {
900 itemsToRemove.add(id);
901 } else {
902 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
903
904 intentDescription = c.getString(intentIndex);
905 intent = null;
906 if (intentDescription != null) {
907 try {
908 intent = Intent.parseUri(intentDescription, 0);
909 } catch (URISyntaxException e) {
910 // Ignore, a live folder might not have a base intent
911 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400912 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800913
914 liveFolderInfo.title = c.getString(titleIndex);
915 liveFolderInfo.id = id;
916 liveFolderInfo.uri = uri;
917 container = c.getInt(containerIndex);
918 liveFolderInfo.container = container;
919 liveFolderInfo.screen = c.getInt(screenIndex);
920 liveFolderInfo.cellX = c.getInt(cellXIndex);
921 liveFolderInfo.cellY = c.getInt(cellYIndex);
922 liveFolderInfo.baseIntent = intent;
923 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
Daniel Sandler8802e962010-05-26 16:28:16 -0400924
925 // check & update map of what's occupied
926 if (!checkItemPlacement(occupied, liveFolderInfo)) {
927 break;
928 }
929
Romain Guy5c16f3e2010-01-12 17:24:58 -0800930 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
931 iconResourceIndex, liveFolderInfo);
932
933 switch (container) {
934 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
935 mItems.add(liveFolderInfo);
936 break;
937 }
938 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400939 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400940 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500941
Joe Onorato9c1289c2009-08-17 11:03:03 -0400942 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
943 // Read all Launcher-specific widget details
944 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800945 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400946
Romain Guy629de3e2010-01-13 12:20:59 -0800947 final AppWidgetProviderInfo provider =
948 widgets.getAppWidgetInfo(appWidgetId);
949
950 if (!isSafeMode && (provider == null || provider.provider == null ||
951 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700952 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
953 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800954 itemsToRemove.add(id);
955 } else {
956 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
957 appWidgetInfo.id = id;
958 appWidgetInfo.screen = c.getInt(screenIndex);
959 appWidgetInfo.cellX = c.getInt(cellXIndex);
960 appWidgetInfo.cellY = c.getInt(cellYIndex);
961 appWidgetInfo.spanX = c.getInt(spanXIndex);
962 appWidgetInfo.spanY = c.getInt(spanYIndex);
963
964 container = c.getInt(containerIndex);
965 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
966 Log.e(TAG, "Widget found where container "
967 + "!= CONTAINER_DESKTOP -- ignoring!");
968 continue;
969 }
970 appWidgetInfo.container = c.getInt(containerIndex);
971
Daniel Sandler8802e962010-05-26 16:28:16 -0400972 // check & update map of what's occupied
973 if (!checkItemPlacement(occupied, appWidgetInfo)) {
974 break;
975 }
976
Romain Guy629de3e2010-01-13 12:20:59 -0800977 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400978 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400979 break;
980 }
981 } catch (Exception e) {
982 Log.w(TAG, "Desktop items loading interrupted:", e);
983 }
984 }
985 } finally {
986 c.close();
987 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800988
989 if (itemsToRemove.size() > 0) {
990 ContentProviderClient client = contentResolver.acquireContentProviderClient(
991 LauncherSettings.Favorites.CONTENT_URI);
992 // Remove dead items
993 for (long id : itemsToRemove) {
994 if (DEBUG_LOADERS) {
995 Log.d(TAG, "Removed id = " + id);
996 }
997 // Don't notify content observers
998 try {
999 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
1000 null, null);
1001 } catch (RemoteException e) {
1002 Log.w(TAG, "Could not remove id = " + id);
1003 }
1004 }
1005 }
1006
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001007 if (DEBUG_LOADERS) {
1008 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Daniel Sandler8802e962010-05-26 16:28:16 -04001009 Log.d(TAG, "workspace layout: ");
1010 for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
1011 String line = "";
1012 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
1013 if (s > 0) {
1014 line += " | ";
1015 }
1016 for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
1017 line += ((occupied[s][x][y] != null) ? "#" : ".");
1018 }
1019 }
1020 Log.d(TAG, "[ " + line + " ]");
1021 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001022 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001023 }
1024
1025 /**
1026 * Read everything out of our database.
1027 */
1028 private void bindWorkspace() {
1029 final long t = SystemClock.uptimeMillis();
1030
1031 // Don't use these two variables in any of the callback runnables.
1032 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -08001033 final Callbacks oldCallbacks = mCallbacks.get();
1034 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001035 // This launcher has exited and nobody bothered to tell us. Just bail.
1036 Log.w(TAG, "LoaderThread running with no launcher");
1037 return;
1038 }
1039
1040 int N;
1041 // Tell the workspace that we're about to start firing items at it
1042 mHandler.post(new Runnable() {
1043 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001044 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001045 if (callbacks != null) {
1046 callbacks.startBinding();
1047 }
1048 }
1049 });
1050 // Add the items to the workspace.
1051 N = mItems.size();
1052 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1053 final int start = i;
1054 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
1055 mHandler.post(new Runnable() {
1056 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001057 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001058 if (callbacks != null) {
1059 callbacks.bindItems(mItems, start, start+chunkSize);
1060 }
1061 }
1062 });
1063 }
Joe Onoratoad72e172009-11-06 16:25:04 -05001064 mHandler.post(new Runnable() {
1065 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001066 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -05001067 if (callbacks != null) {
1068 callbacks.bindFolders(mFolders);
1069 }
1070 }
1071 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001072 // Wait until the queue goes empty.
Daniel Sandler843e8602010-06-07 14:59:01 -04001073 mHandler.post(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001074 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001075 if (DEBUG_LOADERS) {
1076 Log.d(TAG, "Going to start binding widgets soon.");
1077 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001078 }
1079 });
1080 // Bind the widgets, one at a time.
1081 // WARNING: this is calling into the workspace from the background thread,
1082 // but since getCurrentScreen() just returns the int, we should be okay. This
1083 // is just a hint for the order, and if it's wrong, we'll be okay.
1084 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -08001085 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001086 N = mAppWidgets.size();
1087 // once for the current screen
1088 for (int i=0; i<N; i++) {
1089 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1090 if (widget.screen == currentScreen) {
1091 mHandler.post(new Runnable() {
1092 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001093 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001094 if (callbacks != null) {
1095 callbacks.bindAppWidget(widget);
1096 }
1097 }
1098 });
1099 }
1100 }
1101 // once for the other screens
1102 for (int i=0; i<N; i++) {
1103 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1104 if (widget.screen != currentScreen) {
1105 mHandler.post(new Runnable() {
1106 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001107 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001108 if (callbacks != null) {
1109 callbacks.bindAppWidget(widget);
1110 }
1111 }
1112 });
1113 }
1114 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001115 // Tell the workspace that we're done.
1116 mHandler.post(new Runnable() {
1117 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001118 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001119 if (callbacks != null) {
1120 callbacks.finishBindingItems();
1121 }
1122 }
1123 });
1124 // If we're profiling, this is the last thing in the queue.
1125 mHandler.post(new Runnable() {
1126 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001127 if (DEBUG_LOADERS) {
1128 Log.d(TAG, "bound workspace in "
1129 + (SystemClock.uptimeMillis()-t) + "ms");
1130 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001131 }
1132 });
1133 }
1134
Daniel Sandlerdca66122010-04-13 16:23:58 -04001135 private void loadAndBindAllApps() {
1136 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1137
Joe Onoratod65d08e2010-04-20 15:43:37 -04001138 // Don't use these two variables in any of the callback runnables.
1139 // Otherwise we hold a reference to them.
1140 final Callbacks oldCallbacks = mCallbacks.get();
1141 if (oldCallbacks == null) {
1142 // This launcher has exited and nobody bothered to tell us. Just bail.
1143 Log.w(TAG, "LoaderThread running with no launcher (loadAndBindAllApps)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001144 return;
1145 }
1146
Joe Onoratod65d08e2010-04-20 15:43:37 -04001147 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1148 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1149
Joe Onorato0589f0f2010-02-08 13:44:00 -08001150 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001151 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001152
Joe Onoratod65d08e2010-04-20 15:43:37 -04001153 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001154
Joe Onoratod65d08e2010-04-20 15:43:37 -04001155 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001156 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001157 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001158 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001159 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001160 if (i == 0) {
1161 // This needs to happen inside the same lock block as when we
1162 // prepare the first batch for bindAllApplications. Otherwise
1163 // the package changed receiver can come in and double-add
1164 // (or miss one?).
1165 mAllAppsList.clear();
1166 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1167 apps = packageManager.queryIntentActivities(mainIntent, 0);
1168 if (DEBUG_LOADERS) {
1169 Log.d(TAG, "queryIntentActivities took "
1170 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1171 }
1172 if (apps == null) {
1173 return;
1174 }
1175 N = apps.size();
1176 if (DEBUG_LOADERS) {
1177 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1178 }
1179 if (N == 0) {
1180 // There are no apps?!?
1181 return;
1182 }
1183 if (mBatchSize == 0) {
1184 batchSize = N;
1185 } else {
1186 batchSize = mBatchSize;
1187 }
1188
1189 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1190 Collections.sort(apps,
1191 new ResolveInfo.DisplayNameComparator(packageManager));
1192 if (DEBUG_LOADERS) {
1193 Log.d(TAG, "sort took "
Daniel Sandler843e8602010-06-07 14:59:01 -04001194 + (SystemClock.uptimeMillis()-sortTime) + "ms");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001195 }
1196 }
1197
Daniel Sandlerdca66122010-04-13 16:23:58 -04001198 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1199
Joe Onoratod65d08e2010-04-20 15:43:37 -04001200 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001201 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001202 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001203 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001204 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001205 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001206
1207 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001208 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001209 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1210 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1211
1212 mHandler.post(new Runnable() {
1213 public void run() {
1214 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001215 if (callbacks != null) {
1216 if (first) {
1217 mBeforeFirstLoad = false;
1218 callbacks.bindAllApplications(added);
1219 } else {
1220 callbacks.bindAppsAdded(added);
1221 }
1222 if (DEBUG_LOADERS) {
1223 Log.d(TAG, "bound " + added.size() + " apps in "
1224 + (SystemClock.uptimeMillis() - t) + "ms");
1225 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001226 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001227 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001228 }
1229 }
1230 });
1231
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001232 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001233 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001234 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001235 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001236 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001237
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001238 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001239 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001240 if (DEBUG_LOADERS) {
1241 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1242 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001243 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001244 } catch (InterruptedException exc) { }
1245 }
1246 }
1247
1248 if (DEBUG_LOADERS) {
1249 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001250 + (SystemClock.uptimeMillis()-t) + "ms"
1251 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001252 }
1253 }
1254
Joe Onoratobe386092009-11-17 17:32:16 -08001255 public void dumpState() {
1256 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1257 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1258 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1259 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
Daniel Sandler843e8602010-06-07 14:59:01 -04001260 Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
Joe Onoratobe386092009-11-17 17:32:16 -08001261 }
1262 }
1263
1264 public void dumpState() {
1265 Log.d(TAG, "mLoader.mLastWorkspaceSeq=" + mLoader.mLastWorkspaceSeq);
1266 Log.d(TAG, "mLoader.mWorkspaceSeq=" + mLoader.mWorkspaceSeq);
1267 Log.d(TAG, "mLoader.mLastAllAppsSeq=" + mLoader.mLastAllAppsSeq);
1268 Log.d(TAG, "mLoader.mAllAppsSeq=" + mLoader.mAllAppsSeq);
1269 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1270 if (mLoaderThread != null) {
1271 mLoaderThread.dumpState();
1272 } else {
1273 Log.d(TAG, "mLoader.mLoaderThread=null");
1274 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001275 }
1276 }
1277
1278 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001279 * This is called from the code that adds shortcuts from the intent receiver. This
1280 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001281 */
Joe Onorato56d82912010-03-07 14:32:10 -05001282 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001283 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001284 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001285
Joe Onorato56d82912010-03-07 14:32:10 -05001286 /**
1287 * Make an ShortcutInfo object for a shortcut that is an application.
1288 *
1289 * If c is not null, then it will be used to fill in missing data like the title and icon.
1290 */
1291 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1292 Cursor c, int iconIndex, int titleIndex) {
1293 Bitmap icon = null;
1294 final ShortcutInfo info = new ShortcutInfo();
1295
1296 ComponentName componentName = intent.getComponent();
1297 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001298 return null;
1299 }
1300
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001301 // TODO: See if the PackageManager knows about this case. If it doesn't
1302 // then return null & delete this.
1303
Joe Onorato56d82912010-03-07 14:32:10 -05001304 // the resource -- This may implicitly give us back the fallback icon,
1305 // but don't worry about that. All we're doing with usingFallbackIcon is
1306 // to avoid saving lots of copies of that in the database, and most apps
1307 // have icons anyway.
1308 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1309 if (resolveInfo != null) {
1310 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001311 }
Joe Onorato56d82912010-03-07 14:32:10 -05001312 // the db
1313 if (icon == null) {
1314 if (c != null) {
1315 icon = getIconFromCursor(c, iconIndex);
1316 }
1317 }
1318 // the fallback icon
1319 if (icon == null) {
1320 icon = getFallbackIcon();
1321 info.usingFallbackIcon = true;
1322 }
1323 info.setIcon(icon);
1324
1325 // from the resource
1326 if (resolveInfo != null) {
1327 info.title = resolveInfo.activityInfo.loadLabel(manager);
1328 }
1329 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001330 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001331 if (c != null) {
1332 info.title = c.getString(titleIndex);
1333 }
1334 }
1335 // fall back to the class name of the activity
1336 if (info.title == null) {
1337 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001338 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001339 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1340 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001341 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001342
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001343 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001344 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001345 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001346 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001347 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1348 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001349
Joe Onorato56d82912010-03-07 14:32:10 -05001350 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001351 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001352 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001353
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001354 // TODO: If there's an explicit component and we can't install that, delete it.
1355
Joe Onorato56d82912010-03-07 14:32:10 -05001356 info.title = c.getString(titleIndex);
1357
Joe Onorato9c1289c2009-08-17 11:03:03 -04001358 int iconType = c.getInt(iconTypeIndex);
1359 switch (iconType) {
1360 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1361 String packageName = c.getString(iconPackageIndex);
1362 String resourceName = c.getString(iconResourceIndex);
1363 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001364 info.customIcon = false;
1365 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001366 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001367 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001368 if (resources != null) {
1369 final int id = resources.getIdentifier(resourceName, null, null);
1370 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1371 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001372 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001373 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001374 }
Joe Onorato56d82912010-03-07 14:32:10 -05001375 // the db
1376 if (icon == null) {
1377 icon = getIconFromCursor(c, iconIndex);
1378 }
1379 // the fallback icon
1380 if (icon == null) {
1381 icon = getFallbackIcon();
1382 info.usingFallbackIcon = true;
1383 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001384 break;
1385 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001386 icon = getIconFromCursor(c, iconIndex);
1387 if (icon == null) {
1388 icon = getFallbackIcon();
1389 info.customIcon = false;
1390 info.usingFallbackIcon = true;
1391 } else {
1392 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001393 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001394 break;
1395 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001396 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001397 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001398 info.customIcon = false;
1399 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001400 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001401 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001402 return info;
1403 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001404
Joe Onorato56d82912010-03-07 14:32:10 -05001405 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1406 if (false) {
1407 Log.d(TAG, "getIconFromCursor app="
1408 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1409 }
1410 byte[] data = c.getBlob(iconIndex);
1411 try {
1412 return BitmapFactory.decodeByteArray(data, 0, data.length);
1413 } catch (Exception e) {
1414 return null;
1415 }
1416 }
1417
Joe Onorato0589f0f2010-02-08 13:44:00 -08001418 ShortcutInfo addShortcut(Context context, Intent data,
1419 CellLayout.CellInfo cellInfo, boolean notify) {
1420
1421 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1422 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1423 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1424
1425 return info;
1426 }
1427
1428 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1429 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1430 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1431 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1432
1433 Bitmap icon = null;
1434 boolean filtered = false;
1435 boolean customIcon = false;
1436 ShortcutIconResource iconResource = null;
1437
1438 if (bitmap != null && bitmap instanceof Bitmap) {
1439 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1440 filtered = true;
1441 customIcon = true;
1442 } else {
1443 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1444 if (extra != null && extra instanceof ShortcutIconResource) {
1445 try {
1446 iconResource = (ShortcutIconResource) extra;
1447 final PackageManager packageManager = context.getPackageManager();
1448 Resources resources = packageManager.getResourcesForApplication(
1449 iconResource.packageName);
1450 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1451 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1452 } catch (Exception e) {
1453 Log.w(TAG, "Could not load shortcut icon: " + extra);
1454 }
1455 }
1456 }
1457
Joe Onorato0589f0f2010-02-08 13:44:00 -08001458 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001459
1460 if (icon == null) {
1461 icon = getFallbackIcon();
1462 info.usingFallbackIcon = true;
1463 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001464 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001465
Joe Onorato0589f0f2010-02-08 13:44:00 -08001466 info.title = name;
1467 info.intent = intent;
1468 info.customIcon = customIcon;
1469 info.iconResource = iconResource;
1470
1471 return info;
1472 }
1473
Joe Onorato9c1289c2009-08-17 11:03:03 -04001474 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1475 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1476
1477 int iconType = c.getInt(iconTypeIndex);
1478 switch (iconType) {
1479 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1480 String packageName = c.getString(iconPackageIndex);
1481 String resourceName = c.getString(iconResourceIndex);
1482 PackageManager packageManager = context.getPackageManager();
1483 try {
1484 Resources resources = packageManager.getResourcesForApplication(packageName);
1485 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001486 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1487 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001488 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001489 liveFolderInfo.icon = Utilities.createIconBitmap(
1490 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1491 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001492 }
1493 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1494 liveFolderInfo.iconResource.packageName = packageName;
1495 liveFolderInfo.iconResource.resourceName = resourceName;
1496 break;
1497 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001498 liveFolderInfo.icon = Utilities.createIconBitmap(
1499 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1500 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001501 }
1502 }
1503
Joe Onorato56d82912010-03-07 14:32:10 -05001504 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1505 // If this icon doesn't have a custom icon, check to see
1506 // what's stored in the DB, and if it doesn't match what
1507 // we're going to show, store what we are going to show back
1508 // into the DB. We do this so when we're loading, if the
1509 // package manager can't find an icon (for example because
1510 // the app is on SD) then we can use that instead.
1511 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1512 boolean needSave;
1513 byte[] data = c.getBlob(iconIndex);
1514 try {
1515 if (data != null) {
1516 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1517 Bitmap loaded = info.getIcon(mIconCache);
1518 needSave = !saved.sameAs(loaded);
1519 } else {
1520 needSave = true;
1521 }
1522 } catch (Exception e) {
1523 needSave = true;
1524 }
1525 if (needSave) {
1526 Log.d(TAG, "going to save icon bitmap for info=" + info);
1527 // This is slower than is ideal, but this only happens either
1528 // after the froyo OTA or when the app is updated with a new
1529 // icon.
1530 updateItemInDatabase(context, info);
1531 }
1532 }
1533 }
1534
Joe Onorato9c1289c2009-08-17 11:03:03 -04001535 /**
1536 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1537 * or make a new one.
1538 */
1539 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1540 // See if a placeholder was created for us already
1541 FolderInfo folderInfo = folders.get(id);
1542 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1543 // No placeholder -- create a new instance
1544 folderInfo = new UserFolderInfo();
1545 folders.put(id, folderInfo);
1546 }
1547 return (UserFolderInfo) folderInfo;
1548 }
1549
1550 /**
1551 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1552 * new one.
1553 */
1554 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1555 // See if a placeholder was created for us already
1556 FolderInfo folderInfo = folders.get(id);
1557 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1558 // No placeholder -- create a new instance
1559 folderInfo = new LiveFolderInfo();
1560 folders.put(id, folderInfo);
1561 }
1562 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001563 }
1564
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001565 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1566 String label = activityInfo.loadLabel(manager).toString();
1567 if (label == null) {
1568 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1569 if (label == null) {
1570 label = activityInfo.name;
1571 }
1572 }
1573 return label;
1574 }
1575
Joe Onorato9c1289c2009-08-17 11:03:03 -04001576 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001577 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001578 = new Comparator<ApplicationInfo>() {
1579 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1580 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001581 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001582 };
Joe Onoratobe386092009-11-17 17:32:16 -08001583
1584 public void dumpState() {
1585 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1586 Log.d(TAG, "mCallbacks=" + mCallbacks);
1587 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1588 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1589 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1590 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1591 mLoader.dumpState();
1592 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001593}