blob: ce26f37a2546dbb2cdf398bd48748a006009452d [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 Onoratocc67f472010-06-08 10:54:30 -070074 // We start off with everything not loaded. After that, we assume that
75 // our monitoring of the package manager provides all updates and we never
76 // need to do a requery. These are only ever touched from the loader thread.
77 private boolean mWorkspaceLoaded;
78 private boolean mAllAppsLoaded;
79
Joe Onoratod65d08e2010-04-20 15:43:37 -040080 private boolean mBeforeFirstLoad = true; // only access this from main thread
Joe Onorato9c1289c2009-08-17 11:03:03 -040081 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080082
Joe Onoratofad1fb52010-05-04 12:12:41 -070083 private final Object mAllAppsListLock = new Object();
Joe Onorato0589f0f2010-02-08 13:44:00 -080084 private AllAppsList mAllAppsList;
85 private IconCache mIconCache;
86
87 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088
Joe Onorato9c1289c2009-08-17 11:03:03 -040089 public interface Callbacks {
90 public int getCurrentWorkspaceScreen();
91 public void startBinding();
92 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -050093 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -040094 public void finishBindingItems();
95 public void bindAppWidget(LauncherAppWidgetInfo info);
96 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -050097 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
98 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
99 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
Daniel Sandler843e8602010-06-07 14:59:01 -0400100 public boolean isAllAppsVisible();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400101 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102
Joe Onorato0589f0f2010-02-08 13:44:00 -0800103 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400104 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800105 mAllAppsList = new AllAppsList(iconCache);
106 mIconCache = iconCache;
107
108 mDefaultIcon = Utilities.createIconBitmap(
109 app.getPackageManager().getDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400110
111 mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
Joe Onoratod65d08e2010-04-20 15:43:37 -0400112
113 mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800114 }
115
Joe Onorato56d82912010-03-07 14:32:10 -0500116 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800117 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400118 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800119
Joe Onorato9c1289c2009-08-17 11:03:03 -0400120 /**
121 * Adds an item to the DB if it was not created previously, or move it to a new
122 * <container, screen, cellX, cellY>
123 */
124 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
125 int screen, int cellX, int cellY) {
126 if (item.container == ItemInfo.NO_ID) {
127 // From all apps
128 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
129 } else {
130 // From somewhere else
131 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800132 }
133 }
134
135 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400136 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700137 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400138 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
139 int cellX, int cellY) {
140 item.container = container;
141 item.screen = screen;
142 item.cellX = cellX;
143 item.cellY = cellY;
144
145 final ContentValues values = new ContentValues();
146 final ContentResolver cr = context.getContentResolver();
147
148 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
149 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
150 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
151 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
152
153 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700154 }
155
156 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400157 * Returns true if the shortcuts already exists in the database.
158 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800159 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400160 static boolean shortcutExists(Context context, String title, Intent intent) {
161 final ContentResolver cr = context.getContentResolver();
162 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
163 new String[] { "title", "intent" }, "title=? and intent=?",
164 new String[] { title, intent.toUri(0) }, null);
165 boolean result = false;
166 try {
167 result = c.moveToFirst();
168 } finally {
169 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800170 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400171 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700172 }
173
Joe Onorato9c1289c2009-08-17 11:03:03 -0400174 /**
175 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
176 */
177 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
178 final ContentResolver cr = context.getContentResolver();
179 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
180 "_id=? and (itemType=? or itemType=?)",
181 new String[] { String.valueOf(id),
182 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
183 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700184
Joe Onorato9c1289c2009-08-17 11:03:03 -0400185 try {
186 if (c.moveToFirst()) {
187 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
188 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
189 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
190 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
191 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
192 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800193
Joe Onorato9c1289c2009-08-17 11:03:03 -0400194 FolderInfo folderInfo = null;
195 switch (c.getInt(itemTypeIndex)) {
196 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
197 folderInfo = findOrMakeUserFolder(folderList, id);
198 break;
199 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
200 folderInfo = findOrMakeLiveFolder(folderList, id);
201 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700202 }
203
Joe Onorato9c1289c2009-08-17 11:03:03 -0400204 folderInfo.title = c.getString(titleIndex);
205 folderInfo.id = id;
206 folderInfo.container = c.getInt(containerIndex);
207 folderInfo.screen = c.getInt(screenIndex);
208 folderInfo.cellX = c.getInt(cellXIndex);
209 folderInfo.cellY = c.getInt(cellYIndex);
210
211 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700212 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400213 } finally {
214 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700215 }
216
217 return null;
218 }
219
Joe Onorato9c1289c2009-08-17 11:03:03 -0400220 /**
221 * Add an item to the database in a specified container. Sets the container, screen, cellX and
222 * cellY fields of the item. Also assigns an ID to the item.
223 */
224 static void addItemToDatabase(Context context, ItemInfo item, long container,
225 int screen, int cellX, int cellY, boolean notify) {
226 item.container = container;
227 item.screen = screen;
228 item.cellX = cellX;
229 item.cellY = cellY;
230
231 final ContentValues values = new ContentValues();
232 final ContentResolver cr = context.getContentResolver();
233
234 item.onAddToDatabase(values);
235
236 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
237 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
238
239 if (result != null) {
240 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700241 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700242 }
243
Joe Onorato9c1289c2009-08-17 11:03:03 -0400244 /**
245 * Update an item to the database in a specified container.
246 */
247 static void updateItemInDatabase(Context context, ItemInfo item) {
248 final ContentValues values = new ContentValues();
249 final ContentResolver cr = context.getContentResolver();
250
251 item.onAddToDatabase(values);
252
253 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
254 }
255
256 /**
257 * Removes the specified item from the database
258 * @param context
259 * @param item
260 */
261 static void deleteItemFromDatabase(Context context, ItemInfo item) {
262 final ContentResolver cr = context.getContentResolver();
263
264 cr.delete(LauncherSettings.Favorites.getContentUri(item.id, false), null, null);
265 }
266
267 /**
268 * Remove the contents of the specified folder from the database
269 */
270 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
271 final ContentResolver cr = context.getContentResolver();
272
273 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
274 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
275 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
276 }
277
278 /**
279 * Set this as the current Launcher activity object for the loader.
280 */
281 public void initialize(Callbacks callbacks) {
282 synchronized (mLock) {
283 mCallbacks = new WeakReference<Callbacks>(callbacks);
284 }
285 }
286
287 public void startLoader(Context context, boolean isLaunching) {
288 mLoader.startLoader(context, isLaunching);
289 }
290
291 public void stopLoader() {
292 mLoader.stopLoader();
293 }
294
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700295 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400296 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
297 * ACTION_PACKAGE_CHANGED.
298 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400299 public void onReceive(Context context, Intent intent) {
300 // Use the app as the context.
301 context = mApp;
302
Joe Onorato9c1289c2009-08-17 11:03:03 -0400303 ArrayList<ApplicationInfo> added = null;
304 ArrayList<ApplicationInfo> removed = null;
305 ArrayList<ApplicationInfo> modified = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400306
Joe Onoratofad1fb52010-05-04 12:12:41 -0700307 synchronized (mAllAppsListLock) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400308 if (mBeforeFirstLoad) {
309 // If we haven't even loaded yet, don't bother, since we'll just pick
310 // up the changes.
311 return;
312 }
313
Joe Onorato9c1289c2009-08-17 11:03:03 -0400314 final String action = intent.getAction();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400315
Joe Onorato64e6be72010-03-05 15:05:52 -0500316 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
317 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
318 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
319 final String packageName = intent.getData().getSchemeSpecificPart();
320 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400321
Joe Onorato64e6be72010-03-05 15:05:52 -0500322 if (packageName == null || packageName.length() == 0) {
323 // they sent us a bad intent
324 return;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400325 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500326
327 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400328 mAllAppsList.updatePackage(context, packageName);
Joe Onorato64e6be72010-03-05 15:05:52 -0500329 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
330 if (!replacing) {
331 mAllAppsList.removePackage(packageName);
332 }
333 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
334 // later, we will update the package at this time
335 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
336 if (!replacing) {
337 mAllAppsList.addPackage(context, packageName);
338 } else {
339 mAllAppsList.updatePackage(context, packageName);
340 }
341 }
Joe Onorato56d82912010-03-07 14:32:10 -0500342
343 if (mAllAppsList.added.size() > 0) {
344 added = mAllAppsList.added;
345 mAllAppsList.added = new ArrayList<ApplicationInfo>();
346 }
347 if (mAllAppsList.removed.size() > 0) {
348 removed = mAllAppsList.removed;
349 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
350 for (ApplicationInfo info: removed) {
351 mIconCache.remove(info.intent.getComponent());
352 }
353 }
354 if (mAllAppsList.modified.size() > 0) {
355 modified = mAllAppsList.modified;
356 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
357 }
358
359 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
360 if (callbacks == null) {
361 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
362 return;
363 }
364
365 if (added != null) {
366 final ArrayList<ApplicationInfo> addedFinal = added;
367 mHandler.post(new Runnable() {
368 public void run() {
369 callbacks.bindAppsAdded(addedFinal);
370 }
371 });
372 }
373 if (modified != null) {
374 final ArrayList<ApplicationInfo> modifiedFinal = modified;
375 mHandler.post(new Runnable() {
376 public void run() {
377 callbacks.bindAppsUpdated(modifiedFinal);
378 }
379 });
380 }
381 if (removed != null) {
382 final ArrayList<ApplicationInfo> removedFinal = removed;
383 mHandler.post(new Runnable() {
384 public void run() {
385 callbacks.bindAppsRemoved(removedFinal);
386 }
387 });
388 }
Joe Onorato64e6be72010-03-05 15:05:52 -0500389 } else {
390 if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
391 String packages[] = intent.getStringArrayExtra(
392 Intent.EXTRA_CHANGED_PACKAGE_LIST);
393 if (packages == null || packages.length == 0) {
394 return;
395 }
Joe Onoratocc67f472010-06-08 10:54:30 -0700396 synchronized (this) {
397 mAllAppsLoaded = mWorkspaceLoaded = false;
398 }
Joe Onorato56d82912010-03-07 14:32:10 -0500399 startLoader(context, false);
Joe Onorato64e6be72010-03-05 15:05:52 -0500400 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
401 String packages[] = intent.getStringArrayExtra(
402 Intent.EXTRA_CHANGED_PACKAGE_LIST);
403 if (packages == null || packages.length == 0) {
404 return;
405 }
Joe Onoratocc67f472010-06-08 10:54:30 -0700406 synchronized (this) {
407 mAllAppsLoaded = mWorkspaceLoaded = false;
408 }
Joe Onorato56d82912010-03-07 14:32:10 -0500409 startLoader(context, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400410 }
411 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400412 }
413 }
414
415 public class Loader {
416 private static final int ITEMS_CHUNK = 6;
417
418 private LoaderThread mLoaderThread;
419
Romain Guy84f296c2009-11-04 15:00:44 -0800420 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
421 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
Joe Onoratoad72e172009-11-06 16:25:04 -0500422 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400423
424 /**
425 * Call this from the ui thread so the handler is initialized on the correct thread.
426 */
427 public Loader() {
428 }
429
430 public void startLoader(Context context, boolean isLaunching) {
431 synchronized (mLock) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800432 if (DEBUG_LOADERS) {
433 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
434 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400435
Joe Onorato9c1289c2009-08-17 11:03:03 -0400436 // Don't bother to start the thread if we know it's not going to do anything
Joe Onoratoac033302010-04-13 17:19:18 -0700437 if (mCallbacks != null && mCallbacks.get() != null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400438 LoaderThread oldThread = mLoaderThread;
439 if (oldThread != null) {
440 if (oldThread.isLaunching()) {
441 // don't downgrade isLaunching if we're already running
442 isLaunching = true;
443 }
444 oldThread.stopLocked();
445 }
446 mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
447 mLoaderThread.start();
448 }
449 }
450 }
451
452 public void stopLoader() {
453 synchronized (mLock) {
454 if (mLoaderThread != null) {
455 mLoaderThread.stopLocked();
456 }
457 }
458 }
459
Joe Onorato9c1289c2009-08-17 11:03:03 -0400460 /**
461 * Runnable for the thread that loads the contents of the launcher:
462 * - workspace icons
463 * - widgets
464 * - all apps icons
465 */
466 private class LoaderThread extends Thread {
467 private Context mContext;
468 private Thread mWaitThread;
469 private boolean mIsLaunching;
470 private boolean mStopped;
Daniel Sandler843e8602010-06-07 14:59:01 -0400471 private boolean mLoadAndBindStepFinished;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400472
473 LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
474 mContext = context;
475 mWaitThread = waitThread;
476 mIsLaunching = isLaunching;
477 }
478
479 boolean isLaunching() {
480 return mIsLaunching;
481 }
482
483 /**
484 * If another LoaderThread was supplied, we need to wait for that to finish before
485 * we start our processing. This keeps the ordering of the setting and clearing
486 * of the dirty flags correct by making sure we don't start processing stuff until
487 * they've had a chance to re-set them. We do this waiting the worker thread, not
488 * the ui thread to avoid ANRs.
489 */
490 private void waitForOtherThread() {
491 if (mWaitThread != null) {
492 boolean done = false;
493 while (!done) {
494 try {
495 mWaitThread.join();
Joe Onoratoefabe002009-08-28 09:38:18 -0700496 done = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400497 } catch (InterruptedException ex) {
Romain Guy84f296c2009-11-04 15:00:44 -0800498 // Ignore
Joe Onorato9c1289c2009-08-17 11:03:03 -0400499 }
500 }
501 mWaitThread = null;
502 }
503 }
504
Daniel Sandler843e8602010-06-07 14:59:01 -0400505 private void loadAndBindWorkspace() {
Joe Onoratocc67f472010-06-08 10:54:30 -0700506 // Load the workspace
Daniel Sandler843e8602010-06-07 14:59:01 -0400507
Joe Onoratocc67f472010-06-08 10:54:30 -0700508 // Other other threads can unset mWorkspaceLoaded, so atomically set it,
509 // and then if they unset it, or we unset it because of mStopped, it will
510 // be unset.
511 boolean loaded;
512 synchronized (this) {
513 loaded = mWorkspaceLoaded;
514 mWorkspaceLoaded = true;
515 }
516
517 // For now, just always reload the workspace. It's ~100 ms vs. the
518 // binding which takes many hundreds of ms.
519 // We can reconsider.
520 if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindWorkspace loaded=" + loaded);
521 if (true || !loaded) {
522 loadWorkspace();
523 if (mStopped) {
524 mWorkspaceLoaded = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400525 return;
526 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400527 }
528
529 // Bind the workspace
530 bindWorkspace();
Daniel Sandler843e8602010-06-07 14:59:01 -0400531 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400532
Daniel Sandler843e8602010-06-07 14:59:01 -0400533 private void waitForIdle() {
534 // Wait until the either we're stopped or the other threads are done.
535 // This way we don't start loading all apps until the workspace has settled
536 // down.
537 synchronized (LoaderThread.this) {
538 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
539
540 mHandler.postIdle(new Runnable() {
541 public void run() {
542 synchronized (LoaderThread.this) {
543 mLoadAndBindStepFinished = true;
544 if (DEBUG_LOADERS) {
545 Log.d(TAG, "done with previous binding step");
546 }
547 LoaderThread.this.notify();
548 }
549 }
550 });
551
552 while (!mStopped && !mLoadAndBindStepFinished) {
553 try {
554 this.wait();
555 } catch (InterruptedException ex) {
556 // Ignore
557 }
558 }
559 if (DEBUG_LOADERS) {
560 Log.d(TAG, "waited "
561 + (SystemClock.uptimeMillis()-workspaceWaitTime)
562 + "ms for previous step to finish binding");
563 }
564 }
565 }
566
567 public void run() {
568 waitForOtherThread();
569
570 // Optimize for end-user experience: if the Launcher is up and // running with the
571 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
572 // workspace first (default).
573 final Callbacks cbk = mCallbacks.get();
574 final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
575
576 // Elevate priority when Home launches for the first time to avoid
577 // starving at boot time. Staring at a blank home is not cool.
578 synchronized (mLock) {
579 android.os.Process.setThreadPriority(mIsLaunching
580 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
581 }
582
583 if (PROFILE_LOADERS) {
584 android.os.Debug.startMethodTracing("/sdcard/launcher-loaders");
585 }
586
587 if (loadWorkspaceFirst) {
588 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
589 loadAndBindWorkspace();
590 } else {
591 if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700592 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400593 }
594
595 // Whew! Hard work done.
596 synchronized (mLock) {
597 if (mIsLaunching) {
598 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
599 }
600 }
601
602 // second step
603 if (loadWorkspaceFirst) {
604 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700605 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400606 } else {
607 if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
608 loadAndBindWorkspace();
609 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400610
Joe Onorato9c1289c2009-08-17 11:03:03 -0400611 // Clear out this reference, otherwise we end up holding it until all of the
612 // callback runnables are done.
613 mContext = null;
614
615 synchronized (mLock) {
616 // Setting the reference is atomic, but we can't do it inside the other critical
617 // sections.
618 mLoaderThread = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400619 }
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700620
Daniel Sandler843e8602010-06-07 14:59:01 -0400621 if (PROFILE_LOADERS) {
622 android.os.Debug.stopMethodTracing();
623 }
624
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700625 // Trigger a gc to try to clean up after the stuff is done, since the
Joe Onoratocc67f472010-06-08 10:54:30 -0700626 // renderscript allocations aren't charged to the java heap.
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700627 mHandler.post(new Runnable() {
628 public void run() {
629 System.gc();
630 }
631 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400632 }
633
634 public void stopLocked() {
635 synchronized (LoaderThread.this) {
636 mStopped = true;
637 this.notify();
638 }
639 }
640
641 /**
642 * Gets the callbacks object. If we've been stopped, or if the launcher object
Joe Onoratoc131b742010-03-11 15:45:05 -0800643 * has somehow been garbage collected, return null instead. Pass in the Callbacks
644 * object that was around when the deferred message was scheduled, and if there's
645 * a new Callbacks object around then also return null. This will save us from
646 * calling onto it with data that will be ignored.
Joe Onorato9c1289c2009-08-17 11:03:03 -0400647 */
Joe Onoratoc131b742010-03-11 15:45:05 -0800648 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400649 synchronized (mLock) {
650 if (mStopped) {
651 return null;
652 }
653
Joe Onoratoac033302010-04-13 17:19:18 -0700654 if (mCallbacks == null) {
655 return null;
656 }
657
Joe Onorato9c1289c2009-08-17 11:03:03 -0400658 final Callbacks callbacks = mCallbacks.get();
Joe Onoratoc131b742010-03-11 15:45:05 -0800659 if (callbacks != oldCallbacks) {
660 return null;
661 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400662 if (callbacks == null) {
663 Log.w(TAG, "no mCallbacks");
664 return null;
665 }
666
667 return callbacks;
668 }
669 }
670
Daniel Sandler8802e962010-05-26 16:28:16 -0400671 // check & update map of what's occupied; used to discard overlapping/invalid items
672 private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
673 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
674 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
675 if (occupied[item.screen][x][y] != null) {
676 Log.e(TAG, "Error loading shortcut " + item
677 + " into cell (" + item.screen + ":"
678 + x + "," + y
679 + ") occupied by "
680 + occupied[item.screen][x][y]);
681 return false;
682 }
683 }
684 }
685 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
686 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
687 occupied[item.screen][x][y] = item;
688 }
689 }
690 return true;
691 }
692
Joe Onorato9c1289c2009-08-17 11:03:03 -0400693 private void loadWorkspace() {
Daniel Sandler843e8602010-06-07 14:59:01 -0400694 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400695
696 final Context context = mContext;
697 final ContentResolver contentResolver = context.getContentResolver();
698 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800699 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800700 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400701
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400702 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500703 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800704 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400705
Romain Guy5c16f3e2010-01-12 17:24:58 -0800706 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
707
Joe Onorato9c1289c2009-08-17 11:03:03 -0400708 final Cursor c = contentResolver.query(
709 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
710
Daniel Sandler8802e962010-05-26 16:28:16 -0400711 final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
712
Joe Onorato9c1289c2009-08-17 11:03:03 -0400713 try {
714 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
715 final int intentIndex = c.getColumnIndexOrThrow
716 (LauncherSettings.Favorites.INTENT);
717 final int titleIndex = c.getColumnIndexOrThrow
718 (LauncherSettings.Favorites.TITLE);
719 final int iconTypeIndex = c.getColumnIndexOrThrow(
720 LauncherSettings.Favorites.ICON_TYPE);
721 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
722 final int iconPackageIndex = c.getColumnIndexOrThrow(
723 LauncherSettings.Favorites.ICON_PACKAGE);
724 final int iconResourceIndex = c.getColumnIndexOrThrow(
725 LauncherSettings.Favorites.ICON_RESOURCE);
726 final int containerIndex = c.getColumnIndexOrThrow(
727 LauncherSettings.Favorites.CONTAINER);
728 final int itemTypeIndex = c.getColumnIndexOrThrow(
729 LauncherSettings.Favorites.ITEM_TYPE);
730 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
731 LauncherSettings.Favorites.APPWIDGET_ID);
732 final int screenIndex = c.getColumnIndexOrThrow(
733 LauncherSettings.Favorites.SCREEN);
734 final int cellXIndex = c.getColumnIndexOrThrow
735 (LauncherSettings.Favorites.CELLX);
736 final int cellYIndex = c.getColumnIndexOrThrow
737 (LauncherSettings.Favorites.CELLY);
738 final int spanXIndex = c.getColumnIndexOrThrow
739 (LauncherSettings.Favorites.SPANX);
740 final int spanYIndex = c.getColumnIndexOrThrow(
741 LauncherSettings.Favorites.SPANY);
742 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
743 final int displayModeIndex = c.getColumnIndexOrThrow(
744 LauncherSettings.Favorites.DISPLAY_MODE);
745
Joe Onorato0589f0f2010-02-08 13:44:00 -0800746 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400747 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400748 LauncherAppWidgetInfo appWidgetInfo;
749 int container;
750 long id;
751 Intent intent;
752
753 while (!mStopped && c.moveToNext()) {
754 try {
755 int itemType = c.getInt(itemTypeIndex);
756
757 switch (itemType) {
758 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
759 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
760 intentDescription = c.getString(intentIndex);
761 try {
762 intent = Intent.parseUri(intentDescription, 0);
763 } catch (URISyntaxException e) {
764 continue;
765 }
766
767 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500768 info = getShortcutInfo(manager, intent, context, c, iconIndex,
769 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400770 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800771 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500772 iconPackageIndex, iconResourceIndex, iconIndex,
773 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400774 }
775
776 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500777 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400778
Joe Onorato56d82912010-03-07 14:32:10 -0500779 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400780 info.id = c.getLong(idIndex);
781 container = c.getInt(containerIndex);
782 info.container = container;
783 info.screen = c.getInt(screenIndex);
784 info.cellX = c.getInt(cellXIndex);
785 info.cellY = c.getInt(cellYIndex);
786
Daniel Sandler8802e962010-05-26 16:28:16 -0400787 // check & update map of what's occupied
788 if (!checkItemPlacement(occupied, info)) {
789 break;
790 }
791
Joe Onorato9c1289c2009-08-17 11:03:03 -0400792 switch (container) {
793 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
794 mItems.add(info);
795 break;
796 default:
797 // Item is in a user folder
798 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500799 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400800 folderInfo.add(info);
801 break;
802 }
Joe Onorato56d82912010-03-07 14:32:10 -0500803 } else {
804 // Failed to load the shortcut, probably because the
805 // activity manager couldn't resolve it (maybe the app
806 // was uninstalled), or the db row was somehow screwed up.
807 // Delete it.
808 id = c.getLong(idIndex);
809 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
810 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
811 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400812 }
813 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400814
Joe Onoratoad72e172009-11-06 16:25:04 -0500815 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400816 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500817 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400818
819 folderInfo.title = c.getString(titleIndex);
820
821 folderInfo.id = id;
822 container = c.getInt(containerIndex);
823 folderInfo.container = container;
824 folderInfo.screen = c.getInt(screenIndex);
825 folderInfo.cellX = c.getInt(cellXIndex);
826 folderInfo.cellY = c.getInt(cellYIndex);
827
Daniel Sandler8802e962010-05-26 16:28:16 -0400828 // check & update map of what's occupied
829 if (!checkItemPlacement(occupied, folderInfo)) {
830 break;
831 }
832
Joe Onorato9c1289c2009-08-17 11:03:03 -0400833 switch (container) {
834 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
835 mItems.add(folderInfo);
836 break;
837 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500838
839 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400840 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500841
Joe Onorato9c1289c2009-08-17 11:03:03 -0400842 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400843 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800844 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400845
Romain Guy5c16f3e2010-01-12 17:24:58 -0800846 // Make sure the live folder exists
847 final ProviderInfo providerInfo =
848 context.getPackageManager().resolveContentProvider(
849 uri.getAuthority(), 0);
850
851 if (providerInfo == null && !isSafeMode) {
852 itemsToRemove.add(id);
853 } else {
854 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
855
856 intentDescription = c.getString(intentIndex);
857 intent = null;
858 if (intentDescription != null) {
859 try {
860 intent = Intent.parseUri(intentDescription, 0);
861 } catch (URISyntaxException e) {
862 // Ignore, a live folder might not have a base intent
863 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400864 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800865
866 liveFolderInfo.title = c.getString(titleIndex);
867 liveFolderInfo.id = id;
868 liveFolderInfo.uri = uri;
869 container = c.getInt(containerIndex);
870 liveFolderInfo.container = container;
871 liveFolderInfo.screen = c.getInt(screenIndex);
872 liveFolderInfo.cellX = c.getInt(cellXIndex);
873 liveFolderInfo.cellY = c.getInt(cellYIndex);
874 liveFolderInfo.baseIntent = intent;
875 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
Daniel Sandler8802e962010-05-26 16:28:16 -0400876
877 // check & update map of what's occupied
878 if (!checkItemPlacement(occupied, liveFolderInfo)) {
879 break;
880 }
881
Romain Guy5c16f3e2010-01-12 17:24:58 -0800882 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
883 iconResourceIndex, liveFolderInfo);
884
885 switch (container) {
886 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
887 mItems.add(liveFolderInfo);
888 break;
889 }
890 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400891 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400892 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500893
Joe Onorato9c1289c2009-08-17 11:03:03 -0400894 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
895 // Read all Launcher-specific widget details
896 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800897 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400898
Romain Guy629de3e2010-01-13 12:20:59 -0800899 final AppWidgetProviderInfo provider =
900 widgets.getAppWidgetInfo(appWidgetId);
901
902 if (!isSafeMode && (provider == null || provider.provider == null ||
903 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700904 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
905 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800906 itemsToRemove.add(id);
907 } else {
908 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
909 appWidgetInfo.id = id;
910 appWidgetInfo.screen = c.getInt(screenIndex);
911 appWidgetInfo.cellX = c.getInt(cellXIndex);
912 appWidgetInfo.cellY = c.getInt(cellYIndex);
913 appWidgetInfo.spanX = c.getInt(spanXIndex);
914 appWidgetInfo.spanY = c.getInt(spanYIndex);
915
916 container = c.getInt(containerIndex);
917 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
918 Log.e(TAG, "Widget found where container "
919 + "!= CONTAINER_DESKTOP -- ignoring!");
920 continue;
921 }
922 appWidgetInfo.container = c.getInt(containerIndex);
923
Daniel Sandler8802e962010-05-26 16:28:16 -0400924 // check & update map of what's occupied
925 if (!checkItemPlacement(occupied, appWidgetInfo)) {
926 break;
927 }
928
Romain Guy629de3e2010-01-13 12:20:59 -0800929 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400930 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400931 break;
932 }
933 } catch (Exception e) {
934 Log.w(TAG, "Desktop items loading interrupted:", e);
935 }
936 }
937 } finally {
938 c.close();
939 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800940
941 if (itemsToRemove.size() > 0) {
942 ContentProviderClient client = contentResolver.acquireContentProviderClient(
943 LauncherSettings.Favorites.CONTENT_URI);
944 // Remove dead items
945 for (long id : itemsToRemove) {
946 if (DEBUG_LOADERS) {
947 Log.d(TAG, "Removed id = " + id);
948 }
949 // Don't notify content observers
950 try {
951 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
952 null, null);
953 } catch (RemoteException e) {
954 Log.w(TAG, "Could not remove id = " + id);
955 }
956 }
957 }
958
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800959 if (DEBUG_LOADERS) {
960 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Daniel Sandler8802e962010-05-26 16:28:16 -0400961 Log.d(TAG, "workspace layout: ");
962 for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
963 String line = "";
964 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
965 if (s > 0) {
966 line += " | ";
967 }
968 for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
969 line += ((occupied[s][x][y] != null) ? "#" : ".");
970 }
971 }
972 Log.d(TAG, "[ " + line + " ]");
973 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800974 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400975 }
976
977 /**
978 * Read everything out of our database.
979 */
980 private void bindWorkspace() {
981 final long t = SystemClock.uptimeMillis();
982
983 // Don't use these two variables in any of the callback runnables.
984 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800985 final Callbacks oldCallbacks = mCallbacks.get();
986 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400987 // This launcher has exited and nobody bothered to tell us. Just bail.
988 Log.w(TAG, "LoaderThread running with no launcher");
989 return;
990 }
991
992 int N;
993 // Tell the workspace that we're about to start firing items at it
994 mHandler.post(new Runnable() {
995 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -0800996 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400997 if (callbacks != null) {
998 callbacks.startBinding();
999 }
1000 }
1001 });
1002 // Add the items to the workspace.
1003 N = mItems.size();
1004 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1005 final int start = i;
1006 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
1007 mHandler.post(new Runnable() {
1008 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001009 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001010 if (callbacks != null) {
1011 callbacks.bindItems(mItems, start, start+chunkSize);
1012 }
1013 }
1014 });
1015 }
Joe Onoratoad72e172009-11-06 16:25:04 -05001016 mHandler.post(new Runnable() {
1017 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001018 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -05001019 if (callbacks != null) {
1020 callbacks.bindFolders(mFolders);
1021 }
1022 }
1023 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001024 // Wait until the queue goes empty.
Daniel Sandler843e8602010-06-07 14:59:01 -04001025 mHandler.post(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001026 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001027 if (DEBUG_LOADERS) {
1028 Log.d(TAG, "Going to start binding widgets soon.");
1029 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001030 }
1031 });
1032 // Bind the widgets, one at a time.
1033 // WARNING: this is calling into the workspace from the background thread,
1034 // but since getCurrentScreen() just returns the int, we should be okay. This
1035 // is just a hint for the order, and if it's wrong, we'll be okay.
1036 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -08001037 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001038 N = mAppWidgets.size();
1039 // once for the current screen
1040 for (int i=0; i<N; i++) {
1041 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1042 if (widget.screen == currentScreen) {
1043 mHandler.post(new Runnable() {
1044 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001045 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001046 if (callbacks != null) {
1047 callbacks.bindAppWidget(widget);
1048 }
1049 }
1050 });
1051 }
1052 }
1053 // once for the other screens
1054 for (int i=0; i<N; i++) {
1055 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1056 if (widget.screen != currentScreen) {
1057 mHandler.post(new Runnable() {
1058 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001059 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001060 if (callbacks != null) {
1061 callbacks.bindAppWidget(widget);
1062 }
1063 }
1064 });
1065 }
1066 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001067 // Tell the workspace that we're done.
1068 mHandler.post(new Runnable() {
1069 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001070 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001071 if (callbacks != null) {
1072 callbacks.finishBindingItems();
1073 }
1074 }
1075 });
1076 // If we're profiling, this is the last thing in the queue.
1077 mHandler.post(new Runnable() {
1078 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001079 if (DEBUG_LOADERS) {
1080 Log.d(TAG, "bound workspace in "
1081 + (SystemClock.uptimeMillis()-t) + "ms");
1082 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001083 }
1084 });
1085 }
1086
Daniel Sandlerdca66122010-04-13 16:23:58 -04001087 private void loadAndBindAllApps() {
Joe Onoratocc67f472010-06-08 10:54:30 -07001088 // Other other threads can unset mAllAppsLoaded, so atomically set it,
1089 // and then if they unset it, or we unset it because of mStopped, it will
1090 // be unset.
1091 boolean loaded;
1092 synchronized (this) {
1093 loaded = mAllAppsLoaded;
1094 mAllAppsLoaded = true;
1095 }
1096
1097 if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindAllApps loaded=" + loaded);
1098 if (!loaded) {
1099 loadAllAppsByBatch();
1100 if (mStopped) {
1101 mAllAppsLoaded = false;
1102 return;
1103 }
1104 } else {
1105 onlyBindAllApps();
1106 }
1107 }
1108
1109 private void onlyBindAllApps() {
1110 final Callbacks oldCallbacks = mCallbacks.get();
1111 if (oldCallbacks == null) {
1112 // This launcher has exited and nobody bothered to tell us. Just bail.
1113 Log.w(TAG, "LoaderThread running with no launcher (onlyBindAllApps)");
1114 return;
1115 }
1116
1117 // shallow copy
1118 final ArrayList<ApplicationInfo> list
1119 = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
1120 mHandler.post(new Runnable() {
1121 public void run() {
1122 final long t = SystemClock.uptimeMillis();
1123 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1124 if (callbacks != null) {
1125 callbacks.bindAllApplications(list);
1126 }
1127 if (DEBUG_LOADERS) {
1128 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
1129 + (SystemClock.uptimeMillis()-t) + "ms");
1130 }
1131 }
1132 });
1133
1134 }
1135
1136 private void loadAllAppsByBatch() {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001137 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1138
Joe Onoratod65d08e2010-04-20 15:43:37 -04001139 // Don't use these two variables in any of the callback runnables.
1140 // Otherwise we hold a reference to them.
1141 final Callbacks oldCallbacks = mCallbacks.get();
1142 if (oldCallbacks == null) {
1143 // This launcher has exited and nobody bothered to tell us. Just bail.
Joe Onoratocc67f472010-06-08 10:54:30 -07001144 Log.w(TAG, "LoaderThread running with no launcher (loadAllAppsByBatch)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001145 return;
1146 }
1147
Joe Onoratod65d08e2010-04-20 15:43:37 -04001148 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1149 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1150
Joe Onorato0589f0f2010-02-08 13:44:00 -08001151 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001152 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001153
Joe Onoratod65d08e2010-04-20 15:43:37 -04001154 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001155
Joe Onoratod65d08e2010-04-20 15:43:37 -04001156 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001157 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001158 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001159 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001160 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001161 if (i == 0) {
1162 // This needs to happen inside the same lock block as when we
1163 // prepare the first batch for bindAllApplications. Otherwise
1164 // the package changed receiver can come in and double-add
1165 // (or miss one?).
1166 mAllAppsList.clear();
1167 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1168 apps = packageManager.queryIntentActivities(mainIntent, 0);
1169 if (DEBUG_LOADERS) {
1170 Log.d(TAG, "queryIntentActivities took "
1171 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1172 }
1173 if (apps == null) {
1174 return;
1175 }
1176 N = apps.size();
1177 if (DEBUG_LOADERS) {
1178 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1179 }
1180 if (N == 0) {
1181 // There are no apps?!?
1182 return;
1183 }
1184 if (mBatchSize == 0) {
1185 batchSize = N;
1186 } else {
1187 batchSize = mBatchSize;
1188 }
1189
1190 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1191 Collections.sort(apps,
1192 new ResolveInfo.DisplayNameComparator(packageManager));
1193 if (DEBUG_LOADERS) {
1194 Log.d(TAG, "sort took "
Daniel Sandler843e8602010-06-07 14:59:01 -04001195 + (SystemClock.uptimeMillis()-sortTime) + "ms");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001196 }
1197 }
1198
Daniel Sandlerdca66122010-04-13 16:23:58 -04001199 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1200
Joe Onoratod65d08e2010-04-20 15:43:37 -04001201 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001202 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001203 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001204 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001205 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001206 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001207
1208 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001209 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001210 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1211 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1212
1213 mHandler.post(new Runnable() {
1214 public void run() {
1215 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001216 if (callbacks != null) {
1217 if (first) {
1218 mBeforeFirstLoad = false;
1219 callbacks.bindAllApplications(added);
1220 } else {
1221 callbacks.bindAppsAdded(added);
1222 }
1223 if (DEBUG_LOADERS) {
1224 Log.d(TAG, "bound " + added.size() + " apps in "
1225 + (SystemClock.uptimeMillis() - t) + "ms");
1226 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001227 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001228 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001229 }
1230 }
1231 });
1232
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001233 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001234 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001235 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001236 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001237 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001238
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001239 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001240 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001241 if (DEBUG_LOADERS) {
1242 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1243 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001244 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001245 } catch (InterruptedException exc) { }
1246 }
1247 }
1248
1249 if (DEBUG_LOADERS) {
1250 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001251 + (SystemClock.uptimeMillis()-t) + "ms"
1252 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001253 }
1254 }
1255
Joe Onoratobe386092009-11-17 17:32:16 -08001256 public void dumpState() {
1257 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1258 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1259 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1260 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
Daniel Sandler843e8602010-06-07 14:59:01 -04001261 Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
Joe Onoratobe386092009-11-17 17:32:16 -08001262 }
1263 }
1264
1265 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08001266 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1267 if (mLoaderThread != null) {
1268 mLoaderThread.dumpState();
1269 } else {
1270 Log.d(TAG, "mLoader.mLoaderThread=null");
1271 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001272 }
1273 }
1274
1275 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001276 * This is called from the code that adds shortcuts from the intent receiver. This
1277 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001278 */
Joe Onorato56d82912010-03-07 14:32:10 -05001279 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001280 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001281 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001282
Joe Onorato56d82912010-03-07 14:32:10 -05001283 /**
1284 * Make an ShortcutInfo object for a shortcut that is an application.
1285 *
1286 * If c is not null, then it will be used to fill in missing data like the title and icon.
1287 */
1288 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1289 Cursor c, int iconIndex, int titleIndex) {
1290 Bitmap icon = null;
1291 final ShortcutInfo info = new ShortcutInfo();
1292
1293 ComponentName componentName = intent.getComponent();
1294 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001295 return null;
1296 }
1297
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001298 // TODO: See if the PackageManager knows about this case. If it doesn't
1299 // then return null & delete this.
1300
Joe Onorato56d82912010-03-07 14:32:10 -05001301 // the resource -- This may implicitly give us back the fallback icon,
1302 // but don't worry about that. All we're doing with usingFallbackIcon is
1303 // to avoid saving lots of copies of that in the database, and most apps
1304 // have icons anyway.
1305 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1306 if (resolveInfo != null) {
1307 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001308 }
Joe Onorato56d82912010-03-07 14:32:10 -05001309 // the db
1310 if (icon == null) {
1311 if (c != null) {
1312 icon = getIconFromCursor(c, iconIndex);
1313 }
1314 }
1315 // the fallback icon
1316 if (icon == null) {
1317 icon = getFallbackIcon();
1318 info.usingFallbackIcon = true;
1319 }
1320 info.setIcon(icon);
1321
1322 // from the resource
1323 if (resolveInfo != null) {
1324 info.title = resolveInfo.activityInfo.loadLabel(manager);
1325 }
1326 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001327 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001328 if (c != null) {
1329 info.title = c.getString(titleIndex);
1330 }
1331 }
1332 // fall back to the class name of the activity
1333 if (info.title == null) {
1334 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001335 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001336 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1337 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001338 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001339
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001340 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001341 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001342 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001343 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001344 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1345 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001346
Joe Onorato56d82912010-03-07 14:32:10 -05001347 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001348 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001349 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001350
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001351 // TODO: If there's an explicit component and we can't install that, delete it.
1352
Joe Onorato56d82912010-03-07 14:32:10 -05001353 info.title = c.getString(titleIndex);
1354
Joe Onorato9c1289c2009-08-17 11:03:03 -04001355 int iconType = c.getInt(iconTypeIndex);
1356 switch (iconType) {
1357 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1358 String packageName = c.getString(iconPackageIndex);
1359 String resourceName = c.getString(iconResourceIndex);
1360 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001361 info.customIcon = false;
1362 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001363 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001364 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001365 if (resources != null) {
1366 final int id = resources.getIdentifier(resourceName, null, null);
1367 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1368 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001369 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001370 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001371 }
Joe Onorato56d82912010-03-07 14:32:10 -05001372 // the db
1373 if (icon == null) {
1374 icon = getIconFromCursor(c, iconIndex);
1375 }
1376 // the fallback icon
1377 if (icon == null) {
1378 icon = getFallbackIcon();
1379 info.usingFallbackIcon = true;
1380 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001381 break;
1382 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001383 icon = getIconFromCursor(c, iconIndex);
1384 if (icon == null) {
1385 icon = getFallbackIcon();
1386 info.customIcon = false;
1387 info.usingFallbackIcon = true;
1388 } else {
1389 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001390 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001391 break;
1392 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001393 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001394 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001395 info.customIcon = false;
1396 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001397 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001398 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001399 return info;
1400 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001401
Joe Onorato56d82912010-03-07 14:32:10 -05001402 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1403 if (false) {
1404 Log.d(TAG, "getIconFromCursor app="
1405 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1406 }
1407 byte[] data = c.getBlob(iconIndex);
1408 try {
1409 return BitmapFactory.decodeByteArray(data, 0, data.length);
1410 } catch (Exception e) {
1411 return null;
1412 }
1413 }
1414
Joe Onorato0589f0f2010-02-08 13:44:00 -08001415 ShortcutInfo addShortcut(Context context, Intent data,
1416 CellLayout.CellInfo cellInfo, boolean notify) {
1417
1418 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1419 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1420 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1421
1422 return info;
1423 }
1424
1425 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1426 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1427 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1428 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1429
1430 Bitmap icon = null;
1431 boolean filtered = false;
1432 boolean customIcon = false;
1433 ShortcutIconResource iconResource = null;
1434
1435 if (bitmap != null && bitmap instanceof Bitmap) {
1436 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1437 filtered = true;
1438 customIcon = true;
1439 } else {
1440 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1441 if (extra != null && extra instanceof ShortcutIconResource) {
1442 try {
1443 iconResource = (ShortcutIconResource) extra;
1444 final PackageManager packageManager = context.getPackageManager();
1445 Resources resources = packageManager.getResourcesForApplication(
1446 iconResource.packageName);
1447 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1448 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1449 } catch (Exception e) {
1450 Log.w(TAG, "Could not load shortcut icon: " + extra);
1451 }
1452 }
1453 }
1454
Joe Onorato0589f0f2010-02-08 13:44:00 -08001455 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001456
1457 if (icon == null) {
1458 icon = getFallbackIcon();
1459 info.usingFallbackIcon = true;
1460 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001461 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001462
Joe Onorato0589f0f2010-02-08 13:44:00 -08001463 info.title = name;
1464 info.intent = intent;
1465 info.customIcon = customIcon;
1466 info.iconResource = iconResource;
1467
1468 return info;
1469 }
1470
Joe Onorato9c1289c2009-08-17 11:03:03 -04001471 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1472 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1473
1474 int iconType = c.getInt(iconTypeIndex);
1475 switch (iconType) {
1476 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1477 String packageName = c.getString(iconPackageIndex);
1478 String resourceName = c.getString(iconResourceIndex);
1479 PackageManager packageManager = context.getPackageManager();
1480 try {
1481 Resources resources = packageManager.getResourcesForApplication(packageName);
1482 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001483 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1484 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001485 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001486 liveFolderInfo.icon = Utilities.createIconBitmap(
1487 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1488 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001489 }
1490 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1491 liveFolderInfo.iconResource.packageName = packageName;
1492 liveFolderInfo.iconResource.resourceName = resourceName;
1493 break;
1494 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001495 liveFolderInfo.icon = Utilities.createIconBitmap(
1496 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1497 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001498 }
1499 }
1500
Joe Onorato56d82912010-03-07 14:32:10 -05001501 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1502 // If this icon doesn't have a custom icon, check to see
1503 // what's stored in the DB, and if it doesn't match what
1504 // we're going to show, store what we are going to show back
1505 // into the DB. We do this so when we're loading, if the
1506 // package manager can't find an icon (for example because
1507 // the app is on SD) then we can use that instead.
1508 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1509 boolean needSave;
1510 byte[] data = c.getBlob(iconIndex);
1511 try {
1512 if (data != null) {
1513 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1514 Bitmap loaded = info.getIcon(mIconCache);
1515 needSave = !saved.sameAs(loaded);
1516 } else {
1517 needSave = true;
1518 }
1519 } catch (Exception e) {
1520 needSave = true;
1521 }
1522 if (needSave) {
1523 Log.d(TAG, "going to save icon bitmap for info=" + info);
1524 // This is slower than is ideal, but this only happens either
1525 // after the froyo OTA or when the app is updated with a new
1526 // icon.
1527 updateItemInDatabase(context, info);
1528 }
1529 }
1530 }
1531
Joe Onorato9c1289c2009-08-17 11:03:03 -04001532 /**
1533 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1534 * or make a new one.
1535 */
1536 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1537 // See if a placeholder was created for us already
1538 FolderInfo folderInfo = folders.get(id);
1539 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1540 // No placeholder -- create a new instance
1541 folderInfo = new UserFolderInfo();
1542 folders.put(id, folderInfo);
1543 }
1544 return (UserFolderInfo) folderInfo;
1545 }
1546
1547 /**
1548 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1549 * new one.
1550 */
1551 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1552 // See if a placeholder was created for us already
1553 FolderInfo folderInfo = folders.get(id);
1554 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1555 // No placeholder -- create a new instance
1556 folderInfo = new LiveFolderInfo();
1557 folders.put(id, folderInfo);
1558 }
1559 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001560 }
1561
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001562 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1563 String label = activityInfo.loadLabel(manager).toString();
1564 if (label == null) {
1565 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1566 if (label == null) {
1567 label = activityInfo.name;
1568 }
1569 }
1570 return label;
1571 }
1572
Joe Onorato9c1289c2009-08-17 11:03:03 -04001573 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001574 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001575 = new Comparator<ApplicationInfo>() {
1576 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1577 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001578 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001579 };
Joe Onoratobe386092009-11-17 17:32:16 -08001580
1581 public void dumpState() {
1582 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1583 Log.d(TAG, "mCallbacks=" + mCallbacks);
1584 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1585 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1586 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1587 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1588 mLoader.dumpState();
1589 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001590}