blob: 17f75732aa20225c4bad9dd23f00faf6a5179f2a [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 Onorato790c2d92010-06-11 00:14:11 -0700307 if (mBeforeFirstLoad) {
308 // If we haven't even loaded yet, don't bother, since we'll just pick
309 // up the changes.
310 return;
311 }
Joe Onoratof99f8c12009-10-31 17:27:36 -0400312
Joe Onorato790c2d92010-06-11 00:14:11 -0700313 synchronized (mAllAppsListLock) {
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) {
Joe Onorato774b3272010-06-08 17:39:37 -0700673 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
674 return true;
675 }
676
Daniel Sandler8802e962010-05-26 16:28:16 -0400677 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
678 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
679 if (occupied[item.screen][x][y] != null) {
680 Log.e(TAG, "Error loading shortcut " + item
681 + " into cell (" + item.screen + ":"
682 + x + "," + y
683 + ") occupied by "
684 + occupied[item.screen][x][y]);
685 return false;
686 }
687 }
688 }
689 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
690 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
691 occupied[item.screen][x][y] = item;
692 }
693 }
694 return true;
695 }
696
Joe Onorato9c1289c2009-08-17 11:03:03 -0400697 private void loadWorkspace() {
Daniel Sandler843e8602010-06-07 14:59:01 -0400698 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400699
700 final Context context = mContext;
701 final ContentResolver contentResolver = context.getContentResolver();
702 final PackageManager manager = context.getPackageManager();
Romain Guy629de3e2010-01-13 12:20:59 -0800703 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800704 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400705
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400706 mItems.clear();
Joe Onorato511ab642009-11-08 14:14:07 -0500707 mAppWidgets.clear();
Joe Onorato1db7a972009-11-16 18:32:22 -0800708 mFolders.clear();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400709
Romain Guy5c16f3e2010-01-12 17:24:58 -0800710 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
711
Joe Onorato9c1289c2009-08-17 11:03:03 -0400712 final Cursor c = contentResolver.query(
713 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
714
Daniel Sandler8802e962010-05-26 16:28:16 -0400715 final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
716
Joe Onorato9c1289c2009-08-17 11:03:03 -0400717 try {
718 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
719 final int intentIndex = c.getColumnIndexOrThrow
720 (LauncherSettings.Favorites.INTENT);
721 final int titleIndex = c.getColumnIndexOrThrow
722 (LauncherSettings.Favorites.TITLE);
723 final int iconTypeIndex = c.getColumnIndexOrThrow(
724 LauncherSettings.Favorites.ICON_TYPE);
725 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
726 final int iconPackageIndex = c.getColumnIndexOrThrow(
727 LauncherSettings.Favorites.ICON_PACKAGE);
728 final int iconResourceIndex = c.getColumnIndexOrThrow(
729 LauncherSettings.Favorites.ICON_RESOURCE);
730 final int containerIndex = c.getColumnIndexOrThrow(
731 LauncherSettings.Favorites.CONTAINER);
732 final int itemTypeIndex = c.getColumnIndexOrThrow(
733 LauncherSettings.Favorites.ITEM_TYPE);
734 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
735 LauncherSettings.Favorites.APPWIDGET_ID);
736 final int screenIndex = c.getColumnIndexOrThrow(
737 LauncherSettings.Favorites.SCREEN);
738 final int cellXIndex = c.getColumnIndexOrThrow
739 (LauncherSettings.Favorites.CELLX);
740 final int cellYIndex = c.getColumnIndexOrThrow
741 (LauncherSettings.Favorites.CELLY);
742 final int spanXIndex = c.getColumnIndexOrThrow
743 (LauncherSettings.Favorites.SPANX);
744 final int spanYIndex = c.getColumnIndexOrThrow(
745 LauncherSettings.Favorites.SPANY);
746 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
747 final int displayModeIndex = c.getColumnIndexOrThrow(
748 LauncherSettings.Favorites.DISPLAY_MODE);
749
Joe Onorato0589f0f2010-02-08 13:44:00 -0800750 ShortcutInfo info;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400751 String intentDescription;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400752 LauncherAppWidgetInfo appWidgetInfo;
753 int container;
754 long id;
755 Intent intent;
756
757 while (!mStopped && c.moveToNext()) {
758 try {
759 int itemType = c.getInt(itemTypeIndex);
760
761 switch (itemType) {
762 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
763 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
764 intentDescription = c.getString(intentIndex);
765 try {
766 intent = Intent.parseUri(intentDescription, 0);
767 } catch (URISyntaxException e) {
768 continue;
769 }
770
771 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Joe Onorato56d82912010-03-07 14:32:10 -0500772 info = getShortcutInfo(manager, intent, context, c, iconIndex,
773 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400774 } else {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800775 info = getShortcutInfo(c, context, iconTypeIndex,
Joe Onorato56d82912010-03-07 14:32:10 -0500776 iconPackageIndex, iconResourceIndex, iconIndex,
777 titleIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400778 }
779
780 if (info != null) {
Joe Onorato56d82912010-03-07 14:32:10 -0500781 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400782
Joe Onorato56d82912010-03-07 14:32:10 -0500783 info.intent = intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400784 info.id = c.getLong(idIndex);
785 container = c.getInt(containerIndex);
786 info.container = container;
787 info.screen = c.getInt(screenIndex);
788 info.cellX = c.getInt(cellXIndex);
789 info.cellY = c.getInt(cellYIndex);
790
Daniel Sandler8802e962010-05-26 16:28:16 -0400791 // check & update map of what's occupied
792 if (!checkItemPlacement(occupied, info)) {
793 break;
794 }
795
Joe Onorato9c1289c2009-08-17 11:03:03 -0400796 switch (container) {
797 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
798 mItems.add(info);
799 break;
800 default:
801 // Item is in a user folder
802 UserFolderInfo folderInfo =
Joe Onoratoad72e172009-11-06 16:25:04 -0500803 findOrMakeUserFolder(mFolders, container);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400804 folderInfo.add(info);
805 break;
806 }
Joe Onorato56d82912010-03-07 14:32:10 -0500807 } else {
808 // Failed to load the shortcut, probably because the
809 // activity manager couldn't resolve it (maybe the app
810 // was uninstalled), or the db row was somehow screwed up.
811 // Delete it.
812 id = c.getLong(idIndex);
813 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
814 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
815 id, false), null, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400816 }
817 break;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400818
Joe Onoratoad72e172009-11-06 16:25:04 -0500819 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400820 id = c.getLong(idIndex);
Joe Onoratoad72e172009-11-06 16:25:04 -0500821 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400822
823 folderInfo.title = c.getString(titleIndex);
824
825 folderInfo.id = id;
826 container = c.getInt(containerIndex);
827 folderInfo.container = container;
828 folderInfo.screen = c.getInt(screenIndex);
829 folderInfo.cellX = c.getInt(cellXIndex);
830 folderInfo.cellY = c.getInt(cellYIndex);
831
Daniel Sandler8802e962010-05-26 16:28:16 -0400832 // check & update map of what's occupied
833 if (!checkItemPlacement(occupied, folderInfo)) {
834 break;
835 }
836
Joe Onorato9c1289c2009-08-17 11:03:03 -0400837 switch (container) {
838 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
839 mItems.add(folderInfo);
840 break;
841 }
Joe Onoratoad72e172009-11-06 16:25:04 -0500842
843 mFolders.put(folderInfo.id, folderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400844 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500845
Joe Onorato9c1289c2009-08-17 11:03:03 -0400846 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
Joe Onorato9c1289c2009-08-17 11:03:03 -0400847 id = c.getLong(idIndex);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800848 Uri uri = Uri.parse(c.getString(uriIndex));
Joe Onorato9c1289c2009-08-17 11:03:03 -0400849
Romain Guy5c16f3e2010-01-12 17:24:58 -0800850 // Make sure the live folder exists
851 final ProviderInfo providerInfo =
852 context.getPackageManager().resolveContentProvider(
853 uri.getAuthority(), 0);
854
855 if (providerInfo == null && !isSafeMode) {
856 itemsToRemove.add(id);
857 } else {
858 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
859
860 intentDescription = c.getString(intentIndex);
861 intent = null;
862 if (intentDescription != null) {
863 try {
864 intent = Intent.parseUri(intentDescription, 0);
865 } catch (URISyntaxException e) {
866 // Ignore, a live folder might not have a base intent
867 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400868 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800869
870 liveFolderInfo.title = c.getString(titleIndex);
871 liveFolderInfo.id = id;
872 liveFolderInfo.uri = uri;
873 container = c.getInt(containerIndex);
874 liveFolderInfo.container = container;
875 liveFolderInfo.screen = c.getInt(screenIndex);
876 liveFolderInfo.cellX = c.getInt(cellXIndex);
877 liveFolderInfo.cellY = c.getInt(cellYIndex);
878 liveFolderInfo.baseIntent = intent;
879 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
Daniel Sandler8802e962010-05-26 16:28:16 -0400880
881 // check & update map of what's occupied
882 if (!checkItemPlacement(occupied, liveFolderInfo)) {
883 break;
884 }
885
Romain Guy5c16f3e2010-01-12 17:24:58 -0800886 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
887 iconResourceIndex, liveFolderInfo);
888
889 switch (container) {
890 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
891 mItems.add(liveFolderInfo);
892 break;
893 }
894 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400895 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400896 break;
Joe Onoratoad72e172009-11-06 16:25:04 -0500897
Joe Onorato9c1289c2009-08-17 11:03:03 -0400898 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
899 // Read all Launcher-specific widget details
900 int appWidgetId = c.getInt(appWidgetIdIndex);
Romain Guy629de3e2010-01-13 12:20:59 -0800901 id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400902
Romain Guy629de3e2010-01-13 12:20:59 -0800903 final AppWidgetProviderInfo provider =
904 widgets.getAppWidgetInfo(appWidgetId);
905
906 if (!isSafeMode && (provider == null || provider.provider == null ||
907 provider.provider.getPackageName() == null)) {
Joe Onorato8ddc4fd2010-03-17 09:14:50 -0700908 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
909 + id + " appWidgetId=" + appWidgetId);
Romain Guy629de3e2010-01-13 12:20:59 -0800910 itemsToRemove.add(id);
911 } else {
912 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
913 appWidgetInfo.id = id;
914 appWidgetInfo.screen = c.getInt(screenIndex);
915 appWidgetInfo.cellX = c.getInt(cellXIndex);
916 appWidgetInfo.cellY = c.getInt(cellYIndex);
917 appWidgetInfo.spanX = c.getInt(spanXIndex);
918 appWidgetInfo.spanY = c.getInt(spanYIndex);
919
920 container = c.getInt(containerIndex);
921 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
922 Log.e(TAG, "Widget found where container "
923 + "!= CONTAINER_DESKTOP -- ignoring!");
924 continue;
925 }
926 appWidgetInfo.container = c.getInt(containerIndex);
927
Daniel Sandler8802e962010-05-26 16:28:16 -0400928 // check & update map of what's occupied
929 if (!checkItemPlacement(occupied, appWidgetInfo)) {
930 break;
931 }
932
Romain Guy629de3e2010-01-13 12:20:59 -0800933 mAppWidgets.add(appWidgetInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400934 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400935 break;
936 }
937 } catch (Exception e) {
938 Log.w(TAG, "Desktop items loading interrupted:", e);
939 }
940 }
941 } finally {
942 c.close();
943 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800944
945 if (itemsToRemove.size() > 0) {
946 ContentProviderClient client = contentResolver.acquireContentProviderClient(
947 LauncherSettings.Favorites.CONTENT_URI);
948 // Remove dead items
949 for (long id : itemsToRemove) {
950 if (DEBUG_LOADERS) {
951 Log.d(TAG, "Removed id = " + id);
952 }
953 // Don't notify content observers
954 try {
955 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
956 null, null);
957 } catch (RemoteException e) {
958 Log.w(TAG, "Could not remove id = " + id);
959 }
960 }
961 }
962
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800963 if (DEBUG_LOADERS) {
964 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Daniel Sandler8802e962010-05-26 16:28:16 -0400965 Log.d(TAG, "workspace layout: ");
966 for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
967 String line = "";
968 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
969 if (s > 0) {
970 line += " | ";
971 }
972 for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
973 line += ((occupied[s][x][y] != null) ? "#" : ".");
974 }
975 }
976 Log.d(TAG, "[ " + line + " ]");
977 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800978 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400979 }
980
981 /**
982 * Read everything out of our database.
983 */
984 private void bindWorkspace() {
985 final long t = SystemClock.uptimeMillis();
986
987 // Don't use these two variables in any of the callback runnables.
988 // Otherwise we hold a reference to them.
Joe Onoratoc131b742010-03-11 15:45:05 -0800989 final Callbacks oldCallbacks = mCallbacks.get();
990 if (oldCallbacks == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400991 // This launcher has exited and nobody bothered to tell us. Just bail.
992 Log.w(TAG, "LoaderThread running with no launcher");
993 return;
994 }
995
996 int N;
997 // Tell the workspace that we're about to start firing items at it
998 mHandler.post(new Runnable() {
999 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001000 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001001 if (callbacks != null) {
1002 callbacks.startBinding();
1003 }
1004 }
1005 });
1006 // Add the items to the workspace.
1007 N = mItems.size();
1008 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1009 final int start = i;
1010 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
1011 mHandler.post(new Runnable() {
1012 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001013 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001014 if (callbacks != null) {
1015 callbacks.bindItems(mItems, start, start+chunkSize);
1016 }
1017 }
1018 });
1019 }
Joe Onoratoad72e172009-11-06 16:25:04 -05001020 mHandler.post(new Runnable() {
1021 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001022 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratoad72e172009-11-06 16:25:04 -05001023 if (callbacks != null) {
1024 callbacks.bindFolders(mFolders);
1025 }
1026 }
1027 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001028 // Wait until the queue goes empty.
Daniel Sandler843e8602010-06-07 14:59:01 -04001029 mHandler.post(new Runnable() {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001030 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001031 if (DEBUG_LOADERS) {
1032 Log.d(TAG, "Going to start binding widgets soon.");
1033 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001034 }
1035 });
1036 // Bind the widgets, one at a time.
1037 // WARNING: this is calling into the workspace from the background thread,
1038 // but since getCurrentScreen() just returns the int, we should be okay. This
1039 // is just a hint for the order, and if it's wrong, we'll be okay.
1040 // TODO: instead, we should have that push the current screen into here.
Joe Onoratoc131b742010-03-11 15:45:05 -08001041 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001042 N = mAppWidgets.size();
1043 // once for the current screen
1044 for (int i=0; i<N; i++) {
1045 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1046 if (widget.screen == currentScreen) {
1047 mHandler.post(new Runnable() {
1048 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001049 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001050 if (callbacks != null) {
1051 callbacks.bindAppWidget(widget);
1052 }
1053 }
1054 });
1055 }
1056 }
1057 // once for the other screens
1058 for (int i=0; i<N; i++) {
1059 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1060 if (widget.screen != currentScreen) {
1061 mHandler.post(new Runnable() {
1062 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001063 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001064 if (callbacks != null) {
1065 callbacks.bindAppWidget(widget);
1066 }
1067 }
1068 });
1069 }
1070 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001071 // Tell the workspace that we're done.
1072 mHandler.post(new Runnable() {
1073 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001074 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001075 if (callbacks != null) {
1076 callbacks.finishBindingItems();
1077 }
1078 }
1079 });
1080 // If we're profiling, this is the last thing in the queue.
1081 mHandler.post(new Runnable() {
1082 public void run() {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001083 if (DEBUG_LOADERS) {
1084 Log.d(TAG, "bound workspace in "
1085 + (SystemClock.uptimeMillis()-t) + "ms");
1086 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001087 }
1088 });
1089 }
1090
Daniel Sandlerdca66122010-04-13 16:23:58 -04001091 private void loadAndBindAllApps() {
Joe Onoratocc67f472010-06-08 10:54:30 -07001092 // Other other threads can unset mAllAppsLoaded, so atomically set it,
1093 // and then if they unset it, or we unset it because of mStopped, it will
1094 // be unset.
1095 boolean loaded;
1096 synchronized (this) {
1097 loaded = mAllAppsLoaded;
1098 mAllAppsLoaded = true;
1099 }
1100
1101 if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindAllApps loaded=" + loaded);
1102 if (!loaded) {
1103 loadAllAppsByBatch();
1104 if (mStopped) {
1105 mAllAppsLoaded = false;
1106 return;
1107 }
1108 } else {
1109 onlyBindAllApps();
1110 }
1111 }
1112
1113 private void onlyBindAllApps() {
1114 final Callbacks oldCallbacks = mCallbacks.get();
1115 if (oldCallbacks == null) {
1116 // This launcher has exited and nobody bothered to tell us. Just bail.
1117 Log.w(TAG, "LoaderThread running with no launcher (onlyBindAllApps)");
1118 return;
1119 }
1120
1121 // shallow copy
1122 final ArrayList<ApplicationInfo> list
1123 = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
1124 mHandler.post(new Runnable() {
1125 public void run() {
1126 final long t = SystemClock.uptimeMillis();
1127 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1128 if (callbacks != null) {
1129 callbacks.bindAllApplications(list);
1130 }
1131 if (DEBUG_LOADERS) {
1132 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
1133 + (SystemClock.uptimeMillis()-t) + "ms");
1134 }
1135 }
1136 });
1137
1138 }
1139
1140 private void loadAllAppsByBatch() {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001141 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1142
Joe Onoratod65d08e2010-04-20 15:43:37 -04001143 // Don't use these two variables in any of the callback runnables.
1144 // Otherwise we hold a reference to them.
1145 final Callbacks oldCallbacks = mCallbacks.get();
1146 if (oldCallbacks == null) {
1147 // This launcher has exited and nobody bothered to tell us. Just bail.
Joe Onoratocc67f472010-06-08 10:54:30 -07001148 Log.w(TAG, "LoaderThread running with no launcher (loadAllAppsByBatch)");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001149 return;
1150 }
1151
Joe Onoratod65d08e2010-04-20 15:43:37 -04001152 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1153 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1154
Joe Onorato0589f0f2010-02-08 13:44:00 -08001155 final PackageManager packageManager = mContext.getPackageManager();
Joe Onoratod65d08e2010-04-20 15:43:37 -04001156 List<ResolveInfo> apps = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001157
Joe Onoratod65d08e2010-04-20 15:43:37 -04001158 int N = Integer.MAX_VALUE;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001159
Joe Onoratod65d08e2010-04-20 15:43:37 -04001160 int startIndex;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001161 int i=0;
Joe Onoratod65d08e2010-04-20 15:43:37 -04001162 int batchSize = -1;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001163 while (i < N && !mStopped) {
Joe Onoratofad1fb52010-05-04 12:12:41 -07001164 synchronized (mAllAppsListLock) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001165 if (i == 0) {
1166 // This needs to happen inside the same lock block as when we
1167 // prepare the first batch for bindAllApplications. Otherwise
1168 // the package changed receiver can come in and double-add
1169 // (or miss one?).
1170 mAllAppsList.clear();
1171 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1172 apps = packageManager.queryIntentActivities(mainIntent, 0);
1173 if (DEBUG_LOADERS) {
1174 Log.d(TAG, "queryIntentActivities took "
1175 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1176 }
1177 if (apps == null) {
1178 return;
1179 }
1180 N = apps.size();
1181 if (DEBUG_LOADERS) {
1182 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1183 }
1184 if (N == 0) {
1185 // There are no apps?!?
1186 return;
1187 }
1188 if (mBatchSize == 0) {
1189 batchSize = N;
1190 } else {
1191 batchSize = mBatchSize;
1192 }
1193
1194 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1195 Collections.sort(apps,
1196 new ResolveInfo.DisplayNameComparator(packageManager));
1197 if (DEBUG_LOADERS) {
1198 Log.d(TAG, "sort took "
Daniel Sandler843e8602010-06-07 14:59:01 -04001199 + (SystemClock.uptimeMillis()-sortTime) + "ms");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001200 }
1201 }
1202
Daniel Sandlerdca66122010-04-13 16:23:58 -04001203 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1204
Joe Onoratod65d08e2010-04-20 15:43:37 -04001205 startIndex = i;
Daniel Sandlerdca66122010-04-13 16:23:58 -04001206 for (int j=0; i<N && j<batchSize; j++) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001207 // This builds the icon bitmaps.
Joe Onorato0589f0f2010-02-08 13:44:00 -08001208 mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
Daniel Sandlerdca66122010-04-13 16:23:58 -04001209 i++;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001210 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001211
1212 final boolean first = i <= batchSize;
Romain Guy0e74d9f2010-04-28 13:32:43 -07001213 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onoratod65d08e2010-04-20 15:43:37 -04001214 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1215 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1216
1217 mHandler.post(new Runnable() {
1218 public void run() {
1219 final long t = SystemClock.uptimeMillis();
Joe Onorato87d2ca82010-04-21 17:09:18 -04001220 if (callbacks != null) {
1221 if (first) {
1222 mBeforeFirstLoad = false;
1223 callbacks.bindAllApplications(added);
1224 } else {
1225 callbacks.bindAppsAdded(added);
1226 }
1227 if (DEBUG_LOADERS) {
1228 Log.d(TAG, "bound " + added.size() + " apps in "
1229 + (SystemClock.uptimeMillis() - t) + "ms");
1230 }
Joe Onoratod65d08e2010-04-20 15:43:37 -04001231 } else {
Joe Onorato87d2ca82010-04-21 17:09:18 -04001232 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratod65d08e2010-04-20 15:43:37 -04001233 }
1234 }
1235 });
1236
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001237 if (DEBUG_LOADERS) {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001238 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
Daniel Sandlerdca66122010-04-13 16:23:58 -04001239 + (SystemClock.uptimeMillis()-t2) + "ms");
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001240 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001241 }
Daniel Sandlerdca66122010-04-13 16:23:58 -04001242
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001243 if (mAllAppsLoadDelay > 0 && i < N) {
Daniel Sandlerdca66122010-04-13 16:23:58 -04001244 try {
Joe Onoratod65d08e2010-04-20 15:43:37 -04001245 if (DEBUG_LOADERS) {
1246 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1247 }
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001248 Thread.sleep(mAllAppsLoadDelay);
Daniel Sandlerdca66122010-04-13 16:23:58 -04001249 } catch (InterruptedException exc) { }
1250 }
1251 }
1252
1253 if (DEBUG_LOADERS) {
1254 Log.d(TAG, "cached all " + N + " apps in "
Daniel Sandler2ff10b32010-04-16 15:06:06 -04001255 + (SystemClock.uptimeMillis()-t) + "ms"
1256 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onorato9c1289c2009-08-17 11:03:03 -04001257 }
1258 }
1259
Joe Onoratobe386092009-11-17 17:32:16 -08001260 public void dumpState() {
1261 Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
1262 Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
1263 Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
1264 Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
Daniel Sandler843e8602010-06-07 14:59:01 -04001265 Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
Joe Onoratobe386092009-11-17 17:32:16 -08001266 }
1267 }
1268
1269 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08001270 Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
1271 if (mLoaderThread != null) {
1272 mLoaderThread.dumpState();
1273 } else {
1274 Log.d(TAG, "mLoader.mLoaderThread=null");
1275 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001276 }
1277 }
1278
1279 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001280 * This is called from the code that adds shortcuts from the intent receiver. This
1281 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001282 */
Joe Onorato56d82912010-03-07 14:32:10 -05001283 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001284 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001285 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001286
Joe Onorato56d82912010-03-07 14:32:10 -05001287 /**
1288 * Make an ShortcutInfo object for a shortcut that is an application.
1289 *
1290 * If c is not null, then it will be used to fill in missing data like the title and icon.
1291 */
1292 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1293 Cursor c, int iconIndex, int titleIndex) {
1294 Bitmap icon = null;
1295 final ShortcutInfo info = new ShortcutInfo();
1296
1297 ComponentName componentName = intent.getComponent();
1298 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001299 return null;
1300 }
1301
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001302 // TODO: See if the PackageManager knows about this case. If it doesn't
1303 // then return null & delete this.
1304
Joe Onorato56d82912010-03-07 14:32:10 -05001305 // the resource -- This may implicitly give us back the fallback icon,
1306 // but don't worry about that. All we're doing with usingFallbackIcon is
1307 // to avoid saving lots of copies of that in the database, and most apps
1308 // have icons anyway.
1309 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1310 if (resolveInfo != null) {
1311 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001312 }
Joe Onorato56d82912010-03-07 14:32:10 -05001313 // the db
1314 if (icon == null) {
1315 if (c != null) {
1316 icon = getIconFromCursor(c, iconIndex);
1317 }
1318 }
1319 // the fallback icon
1320 if (icon == null) {
1321 icon = getFallbackIcon();
1322 info.usingFallbackIcon = true;
1323 }
1324 info.setIcon(icon);
1325
1326 // from the resource
1327 if (resolveInfo != null) {
1328 info.title = resolveInfo.activityInfo.loadLabel(manager);
1329 }
1330 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001331 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001332 if (c != null) {
1333 info.title = c.getString(titleIndex);
1334 }
1335 }
1336 // fall back to the class name of the activity
1337 if (info.title == null) {
1338 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001339 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001340 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1341 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001342 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001343
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001344 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001345 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001346 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001347 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001348 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1349 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001350
Joe Onorato56d82912010-03-07 14:32:10 -05001351 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001352 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001353 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001354
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001355 // TODO: If there's an explicit component and we can't install that, delete it.
1356
Joe Onorato56d82912010-03-07 14:32:10 -05001357 info.title = c.getString(titleIndex);
1358
Joe Onorato9c1289c2009-08-17 11:03:03 -04001359 int iconType = c.getInt(iconTypeIndex);
1360 switch (iconType) {
1361 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1362 String packageName = c.getString(iconPackageIndex);
1363 String resourceName = c.getString(iconResourceIndex);
1364 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001365 info.customIcon = false;
1366 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001367 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001368 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001369 if (resources != null) {
1370 final int id = resources.getIdentifier(resourceName, null, null);
1371 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1372 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001373 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001374 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001375 }
Joe Onorato56d82912010-03-07 14:32:10 -05001376 // the db
1377 if (icon == null) {
1378 icon = getIconFromCursor(c, iconIndex);
1379 }
1380 // the fallback icon
1381 if (icon == null) {
1382 icon = getFallbackIcon();
1383 info.usingFallbackIcon = true;
1384 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001385 break;
1386 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001387 icon = getIconFromCursor(c, iconIndex);
1388 if (icon == null) {
1389 icon = getFallbackIcon();
1390 info.customIcon = false;
1391 info.usingFallbackIcon = true;
1392 } else {
1393 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001394 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001395 break;
1396 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001397 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001398 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001399 info.customIcon = false;
1400 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001401 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001402 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001403 return info;
1404 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001405
Joe Onorato56d82912010-03-07 14:32:10 -05001406 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1407 if (false) {
1408 Log.d(TAG, "getIconFromCursor app="
1409 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1410 }
1411 byte[] data = c.getBlob(iconIndex);
1412 try {
1413 return BitmapFactory.decodeByteArray(data, 0, data.length);
1414 } catch (Exception e) {
1415 return null;
1416 }
1417 }
1418
Joe Onorato0589f0f2010-02-08 13:44:00 -08001419 ShortcutInfo addShortcut(Context context, Intent data,
1420 CellLayout.CellInfo cellInfo, boolean notify) {
1421
1422 final ShortcutInfo info = infoFromShortcutIntent(context, data);
1423 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
1424 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
1425
1426 return info;
1427 }
1428
1429 private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
1430 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1431 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1432 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1433
1434 Bitmap icon = null;
1435 boolean filtered = false;
1436 boolean customIcon = false;
1437 ShortcutIconResource iconResource = null;
1438
1439 if (bitmap != null && bitmap instanceof Bitmap) {
1440 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1441 filtered = true;
1442 customIcon = true;
1443 } else {
1444 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1445 if (extra != null && extra instanceof ShortcutIconResource) {
1446 try {
1447 iconResource = (ShortcutIconResource) extra;
1448 final PackageManager packageManager = context.getPackageManager();
1449 Resources resources = packageManager.getResourcesForApplication(
1450 iconResource.packageName);
1451 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
1452 icon = Utilities.createIconBitmap(resources.getDrawable(id), context);
1453 } catch (Exception e) {
1454 Log.w(TAG, "Could not load shortcut icon: " + extra);
1455 }
1456 }
1457 }
1458
Joe Onorato0589f0f2010-02-08 13:44:00 -08001459 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001460
1461 if (icon == null) {
1462 icon = getFallbackIcon();
1463 info.usingFallbackIcon = true;
1464 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001465 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001466
Joe Onorato0589f0f2010-02-08 13:44:00 -08001467 info.title = name;
1468 info.intent = intent;
1469 info.customIcon = customIcon;
1470 info.iconResource = iconResource;
1471
1472 return info;
1473 }
1474
Joe Onorato9c1289c2009-08-17 11:03:03 -04001475 private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
1476 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1477
1478 int iconType = c.getInt(iconTypeIndex);
1479 switch (iconType) {
1480 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1481 String packageName = c.getString(iconPackageIndex);
1482 String resourceName = c.getString(iconResourceIndex);
1483 PackageManager packageManager = context.getPackageManager();
1484 try {
1485 Resources resources = packageManager.getResourcesForApplication(packageName);
1486 final int id = resources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001487 liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id),
1488 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001489 } catch (Exception e) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001490 liveFolderInfo.icon = Utilities.createIconBitmap(
1491 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1492 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001493 }
1494 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1495 liveFolderInfo.iconResource.packageName = packageName;
1496 liveFolderInfo.iconResource.resourceName = resourceName;
1497 break;
1498 default:
Joe Onorato0589f0f2010-02-08 13:44:00 -08001499 liveFolderInfo.icon = Utilities.createIconBitmap(
1500 context.getResources().getDrawable(R.drawable.ic_launcher_folder),
1501 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001502 }
1503 }
1504
Joe Onorato56d82912010-03-07 14:32:10 -05001505 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1506 // If this icon doesn't have a custom icon, check to see
1507 // what's stored in the DB, and if it doesn't match what
1508 // we're going to show, store what we are going to show back
1509 // into the DB. We do this so when we're loading, if the
1510 // package manager can't find an icon (for example because
1511 // the app is on SD) then we can use that instead.
1512 if (info.onExternalStorage && !info.customIcon && !info.usingFallbackIcon) {
1513 boolean needSave;
1514 byte[] data = c.getBlob(iconIndex);
1515 try {
1516 if (data != null) {
1517 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1518 Bitmap loaded = info.getIcon(mIconCache);
1519 needSave = !saved.sameAs(loaded);
1520 } else {
1521 needSave = true;
1522 }
1523 } catch (Exception e) {
1524 needSave = true;
1525 }
1526 if (needSave) {
1527 Log.d(TAG, "going to save icon bitmap for info=" + info);
1528 // This is slower than is ideal, but this only happens either
1529 // after the froyo OTA or when the app is updated with a new
1530 // icon.
1531 updateItemInDatabase(context, info);
1532 }
1533 }
1534 }
1535
Joe Onorato9c1289c2009-08-17 11:03:03 -04001536 /**
1537 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1538 * or make a new one.
1539 */
1540 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1541 // See if a placeholder was created for us already
1542 FolderInfo folderInfo = folders.get(id);
1543 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1544 // No placeholder -- create a new instance
1545 folderInfo = new UserFolderInfo();
1546 folders.put(id, folderInfo);
1547 }
1548 return (UserFolderInfo) folderInfo;
1549 }
1550
1551 /**
1552 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1553 * new one.
1554 */
1555 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1556 // See if a placeholder was created for us already
1557 FolderInfo folderInfo = folders.get(id);
1558 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1559 // No placeholder -- create a new instance
1560 folderInfo = new LiveFolderInfo();
1561 folders.put(id, folderInfo);
1562 }
1563 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001564 }
1565
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001566 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1567 String label = activityInfo.loadLabel(manager).toString();
1568 if (label == null) {
1569 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1570 if (label == null) {
1571 label = activityInfo.name;
1572 }
1573 }
1574 return label;
1575 }
1576
Joe Onorato9c1289c2009-08-17 11:03:03 -04001577 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001578 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001579 = new Comparator<ApplicationInfo>() {
1580 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1581 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001582 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001583 };
Joe Onoratobe386092009-11-17 17:32:16 -08001584
1585 public void dumpState() {
1586 Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
1587 Log.d(TAG, "mCallbacks=" + mCallbacks);
1588 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1589 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1590 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1591 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
1592 mLoader.dumpState();
1593 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001594}