blob: 713268ad6c36e9ba7461a95a9f56312709571fbf [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
Winson Chung68846fd2010-10-29 11:00:27 -070019import java.lang.ref.WeakReference;
20import java.net.URISyntaxException;
21import java.text.Collator;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.Comparator;
25import java.util.HashMap;
26import java.util.List;
Winson Chungaafa03c2010-06-11 17:34:16 -070027
Romain Guy629de3e2010-01-13 12:20:59 -080028import android.appwidget.AppWidgetManager;
29import android.appwidget.AppWidgetProviderInfo;
Joe Onoratof99f8c12009-10-31 17:27:36 -040030import android.content.BroadcastReceiver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import android.content.ComponentName;
Romain Guy5c16f3e2010-01-12 17:24:58 -080032import android.content.ContentProviderClient;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.ContentResolver;
34import android.content.ContentValues;
Winson Chungaafa03c2010-06-11 17:34:16 -070035import android.content.Context;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080037import android.content.Intent.ShortcutIconResource;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.content.pm.ActivityInfo;
39import android.content.pm.PackageManager;
Romain Guy5c16f3e2010-01-12 17:24:58 -080040import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041import android.content.pm.ResolveInfo;
42import android.content.res.Resources;
43import android.database.Cursor;
44import android.graphics.Bitmap;
45import android.graphics.BitmapFactory;
46import android.net.Uri;
Joe Onorato36115782010-06-17 13:28:48 -040047import android.os.Handler;
48import android.os.HandlerThread;
Joe Onorato0589f0f2010-02-08 13:44:00 -080049import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import android.os.Process;
Winson Chungaafa03c2010-06-11 17:34:16 -070051import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040052import android.os.SystemClock;
Winson Chungaafa03c2010-06-11 17:34:16 -070053import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054
Winson Chung68846fd2010-10-29 11:00:27 -070055import com.android.launcher.R;
56import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
Romain Guyedcce092010-03-04 13:03:17 -080057
The Android Open Source Project31dd5032009-03-03 19:32:27 -080058/**
59 * Maintains in-memory state of the Launcher. It is expected that there should be only one
60 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070061 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040063public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080064 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040065 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070066
Joe Onorato36115782010-06-17 13:28:48 -040067 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
Joe Onoratod65d08e2010-04-20 15:43:37 -040068 private int mBatchSize; // 0 is all apps at once
Daniel Sandler2ff10b32010-04-16 15:06:06 -040069 private int mAllAppsLoadDelay; // milliseconds between batches
Daniel Sandlerdca66122010-04-13 16:23:58 -040070
Joe Onoratof99f8c12009-10-31 17:27:36 -040071 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040072 private final Object mLock = new Object();
73 private DeferredHandler mHandler = new DeferredHandler();
Joe Onorato36115782010-06-17 13:28:48 -040074 private LoaderTask mLoaderTask;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075
Brad Fitzpatrick700889f2010-10-11 09:40:44 -070076 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
77 static {
78 sWorkerThread.start();
79 }
80 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
81
Joe Onoratocc67f472010-06-08 10:54:30 -070082 // We start off with everything not loaded. After that, we assume that
83 // our monitoring of the package manager provides all updates and we never
84 // need to do a requery. These are only ever touched from the loader thread.
85 private boolean mWorkspaceLoaded;
86 private boolean mAllAppsLoaded;
87
Joe Onorato9c1289c2009-08-17 11:03:03 -040088 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089
Joe Onorato36115782010-06-17 13:28:48 -040090 private AllAppsList mAllAppsList; // only access in worker thread
Joe Onorato0589f0f2010-02-08 13:44:00 -080091 private IconCache mIconCache;
Joe Onorato36115782010-06-17 13:28:48 -040092 final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
93 final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
94 final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
Joe Onorato0589f0f2010-02-08 13:44:00 -080095
96 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080097
Adam Cohend22015c2010-07-26 22:02:18 -070098 private static int mCellCountX;
99 private static int mCellCountY;
Winson Chungaafa03c2010-06-11 17:34:16 -0700100
Joe Onorato9c1289c2009-08-17 11:03:03 -0400101 public interface Callbacks {
Joe Onoratoef2efcf2010-10-27 13:21:00 -0700102 public boolean setLoadOnResume();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400103 public int getCurrentWorkspaceScreen();
104 public void startBinding();
105 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -0500106 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400107 public void finishBindingItems();
108 public void bindAppWidget(LauncherAppWidgetInfo info);
109 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -0500110 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
111 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
Joe Onorato36115782010-06-17 13:28:48 -0400112 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
Winson Chung80baf5a2010-08-09 16:03:15 -0700113 public void bindPackagesUpdated();
Daniel Sandler843e8602010-06-07 14:59:01 -0400114 public boolean isAllAppsVisible();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400115 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800116
Joe Onorato0589f0f2010-02-08 13:44:00 -0800117 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onoratof99f8c12009-10-31 17:27:36 -0400118 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800119 mAllAppsList = new AllAppsList(iconCache);
120 mIconCache = iconCache;
121
122 mDefaultIcon = Utilities.createIconBitmap(
Michael Jurkac9a96192010-11-01 11:52:08 -0700123 mIconCache.getFullResDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400124
125 mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
Joe Onoratod65d08e2010-04-20 15:43:37 -0400126
127 mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800128 }
129
Joe Onorato56d82912010-03-07 14:32:10 -0500130 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800131 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400132 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800133
Joe Onorato9c1289c2009-08-17 11:03:03 -0400134 /**
135 * Adds an item to the DB if it was not created previously, or move it to a new
136 * <container, screen, cellX, cellY>
137 */
138 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
139 int screen, int cellX, int cellY) {
140 if (item.container == ItemInfo.NO_ID) {
141 // From all apps
142 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
143 } else {
144 // From somewhere else
145 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800146 }
147 }
148
149 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400150 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700151 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400152 static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
153 int cellX, int cellY) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700154
Joe Onorato9c1289c2009-08-17 11:03:03 -0400155 item.container = container;
156 item.screen = screen;
157 item.cellX = cellX;
158 item.cellY = cellY;
159
Brad Fitzpatrickade2f812010-10-10 15:42:06 -0700160 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400161 final ContentValues values = new ContentValues();
162 final ContentResolver cr = context.getContentResolver();
163
164 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Adam Cohend22015c2010-07-26 22:02:18 -0700165 values.put(LauncherSettings.Favorites.CELLX, cellX);
166 values.put(LauncherSettings.Favorites.CELLY, cellY);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400167 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
168
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700169 sWorker.post(new Runnable() {
170 public void run() {
171 cr.update(uri, values, null, null);
172 }
173 });
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700174 }
175
176 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400177 * Returns true if the shortcuts already exists in the database.
178 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800179 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400180 static boolean shortcutExists(Context context, String title, Intent intent) {
181 final ContentResolver cr = context.getContentResolver();
182 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
183 new String[] { "title", "intent" }, "title=? and intent=?",
184 new String[] { title, intent.toUri(0) }, null);
185 boolean result = false;
186 try {
187 result = c.moveToFirst();
188 } finally {
189 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800190 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400191 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700192 }
193
Joe Onorato9c1289c2009-08-17 11:03:03 -0400194 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700195 * Returns an ItemInfo array containing all the items in the LauncherModel.
196 * The ItemInfo.id is not set through this function.
197 */
198 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
199 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
200 final ContentResolver cr = context.getContentResolver();
201 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
202 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
203 LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
204 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
205
206 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
207 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
208 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
209 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
210 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
211 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
212 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
213
214 try {
215 while (c.moveToNext()) {
216 ItemInfo item = new ItemInfo();
217 item.cellX = c.getInt(cellXIndex);
218 item.cellY = c.getInt(cellYIndex);
219 item.spanX = c.getInt(spanXIndex);
220 item.spanY = c.getInt(spanYIndex);
221 item.container = c.getInt(containerIndex);
222 item.itemType = c.getInt(itemTypeIndex);
223 item.screen = c.getInt(screenIndex);
224
225 items.add(item);
226 }
227 } catch (Exception e) {
228 items.clear();
229 } finally {
230 c.close();
231 }
232
233 return items;
234 }
235
236 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400237 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
238 */
239 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
240 final ContentResolver cr = context.getContentResolver();
241 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
242 "_id=? and (itemType=? or itemType=?)",
243 new String[] { String.valueOf(id),
244 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER),
245 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER) }, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700246
Joe Onorato9c1289c2009-08-17 11:03:03 -0400247 try {
248 if (c.moveToFirst()) {
249 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
250 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
251 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
252 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
253 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
254 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800255
Joe Onorato9c1289c2009-08-17 11:03:03 -0400256 FolderInfo folderInfo = null;
257 switch (c.getInt(itemTypeIndex)) {
258 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
259 folderInfo = findOrMakeUserFolder(folderList, id);
260 break;
261 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
262 folderInfo = findOrMakeLiveFolder(folderList, id);
263 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700264 }
265
Joe Onorato9c1289c2009-08-17 11:03:03 -0400266 folderInfo.title = c.getString(titleIndex);
267 folderInfo.id = id;
268 folderInfo.container = c.getInt(containerIndex);
269 folderInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700270 folderInfo.cellX = c.getInt(cellXIndex);
271 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400272
273 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700274 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400275 } finally {
276 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700277 }
278
279 return null;
280 }
281
Joe Onorato9c1289c2009-08-17 11:03:03 -0400282 /**
283 * Add an item to the database in a specified container. Sets the container, screen, cellX and
284 * cellY fields of the item. Also assigns an ID to the item.
285 */
286 static void addItemToDatabase(Context context, ItemInfo item, long container,
287 int screen, int cellX, int cellY, boolean notify) {
288 item.container = container;
289 item.screen = screen;
290 item.cellX = cellX;
291 item.cellY = cellY;
292
293 final ContentValues values = new ContentValues();
294 final ContentResolver cr = context.getContentResolver();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400295 item.onAddToDatabase(values);
296
Adam Cohend22015c2010-07-26 22:02:18 -0700297 item.updateValuesWithCoordinates(values, cellX, cellY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700298
Joe Onorato9c1289c2009-08-17 11:03:03 -0400299 Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
300 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
301
302 if (result != null) {
303 item.id = Integer.parseInt(result.getPathSegments().get(1));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700304 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700305 }
306
Joe Onorato9c1289c2009-08-17 11:03:03 -0400307 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700308 * Creates a new unique child id, for a given cell span across all layouts.
309 */
Michael Jurka845ba3b2010-09-28 17:09:46 -0700310 static int getCellLayoutChildId(
311 int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) {
312 return ((cellId & 0xFF) << 24)
313 | (screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
Winson Chungaafa03c2010-06-11 17:34:16 -0700314 }
315
Adam Cohend22015c2010-07-26 22:02:18 -0700316 static int getCellCountX() {
317 return mCellCountX;
Winson Chungaafa03c2010-06-11 17:34:16 -0700318 }
319
Adam Cohend22015c2010-07-26 22:02:18 -0700320 static int getCellCountY() {
321 return mCellCountY;
Winson Chungaafa03c2010-06-11 17:34:16 -0700322 }
323
324 /**
325 * Updates the model orientation helper to take into account the current layout dimensions
326 * when performing local/canonical coordinate transformations.
327 */
328 static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) {
Adam Cohend22015c2010-07-26 22:02:18 -0700329 mCellCountX = shortAxisCellCount;
330 mCellCountY = longAxisCellCount;
Winson Chungaafa03c2010-06-11 17:34:16 -0700331 }
332
333 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400334 * Update an item to the database in a specified container.
335 */
336 static void updateItemInDatabase(Context context, ItemInfo item) {
337 final ContentValues values = new ContentValues();
338 final ContentResolver cr = context.getContentResolver();
339
340 item.onAddToDatabase(values);
Adam Cohend22015c2010-07-26 22:02:18 -0700341 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700342
Joe Onorato9c1289c2009-08-17 11:03:03 -0400343 cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
344 }
345
346 /**
347 * Removes the specified item from the database
348 * @param context
349 * @param item
350 */
351 static void deleteItemFromDatabase(Context context, ItemInfo item) {
352 final ContentResolver cr = context.getContentResolver();
Brad Fitzpatrick73013bf2010-09-14 12:15:32 -0700353 final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700354 sWorker.post(new Runnable() {
355 public void run() {
356 cr.delete(uriToDelete, null, null);
357 }
358 });
Joe Onorato9c1289c2009-08-17 11:03:03 -0400359 }
360
361 /**
362 * Remove the contents of the specified folder from the database
363 */
364 static void deleteUserFolderContentsFromDatabase(Context context, UserFolderInfo info) {
365 final ContentResolver cr = context.getContentResolver();
366
367 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
368 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
369 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
370 }
371
372 /**
373 * Set this as the current Launcher activity object for the loader.
374 */
375 public void initialize(Callbacks callbacks) {
376 synchronized (mLock) {
377 mCallbacks = new WeakReference<Callbacks>(callbacks);
378 }
379 }
380
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700381 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400382 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
383 * ACTION_PACKAGE_CHANGED.
384 */
Joe Onoratof99f8c12009-10-31 17:27:36 -0400385 public void onReceive(Context context, Intent intent) {
Joe Onorato36115782010-06-17 13:28:48 -0400386 if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
Winson Chungaafa03c2010-06-11 17:34:16 -0700387
Joe Onorato36115782010-06-17 13:28:48 -0400388 final String action = intent.getAction();
Joe Onoratof99f8c12009-10-31 17:27:36 -0400389
Joe Onorato36115782010-06-17 13:28:48 -0400390 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
391 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
392 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
393 final String packageName = intent.getData().getSchemeSpecificPart();
394 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400395
Joe Onorato36115782010-06-17 13:28:48 -0400396 int op = PackageUpdatedTask.OP_NONE;
397
398 if (packageName == null || packageName.length() == 0) {
399 // they sent us a bad intent
400 return;
401 }
402
403 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
404 op = PackageUpdatedTask.OP_UPDATE;
405 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
406 if (!replacing) {
407 op = PackageUpdatedTask.OP_REMOVE;
408 }
409 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
410 // later, we will update the package at this time
411 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
412 if (!replacing) {
413 op = PackageUpdatedTask.OP_ADD;
414 } else {
415 op = PackageUpdatedTask.OP_UPDATE;
416 }
417 }
418
419 if (op != PackageUpdatedTask.OP_NONE) {
420 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
421 }
422
423 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Joe Onoratocec58332010-10-07 14:37:40 -0400424 // First, schedule to add these apps back in.
425 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
426 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
427 // Then, rebind everything.
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700428 startLoaderFromBackground();
Joe Onorato36115782010-06-17 13:28:48 -0400429 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
430 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
431 enqueuePackageUpdated(new PackageUpdatedTask(
432 PackageUpdatedTask.OP_UNAVAILABLE, packages));
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700433 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
434 // If we have changed locale we need to clear out the labels in all apps.
435 // Do this here because if the launcher activity is running it will be restarted.
436 // If it's not running startLoaderFromBackground will merely tell it that it needs
437 // to reload. Either way, mAllAppsLoaded will be cleared so it re-reads everything
438 // next time.
439 mAllAppsLoaded = false;
440 startLoaderFromBackground();
441 }
442 }
443
444 /**
445 * When the launcher is in the background, it's possible for it to miss paired
446 * configuration changes. So whenever we trigger the loader from the background
447 * tell the launcher that it needs to re-run the loader when it comes back instead
448 * of doing it now.
449 */
450 public void startLoaderFromBackground() {
451 boolean runLoader = false;
452 if (mCallbacks != null) {
453 Callbacks callbacks = mCallbacks.get();
454 if (callbacks != null) {
455 // Only actually run the loader if they're not paused.
456 if (!callbacks.setLoadOnResume()) {
457 runLoader = true;
458 }
459 }
460 }
461 if (runLoader) {
462 startLoader(mApp, false);
Joe Onorato790c2d92010-06-11 00:14:11 -0700463 }
Joe Onorato36115782010-06-17 13:28:48 -0400464 }
Joe Onoratof99f8c12009-10-31 17:27:36 -0400465
Joe Onorato36115782010-06-17 13:28:48 -0400466 public void startLoader(Context context, boolean isLaunching) {
467 synchronized (mLock) {
468 if (DEBUG_LOADERS) {
469 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
470 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400471
Joe Onorato36115782010-06-17 13:28:48 -0400472 // Don't bother to start the thread if we know it's not going to do anything
473 if (mCallbacks != null && mCallbacks.get() != null) {
474 // If there is already one running, tell it to stop.
475 LoaderTask oldTask = mLoaderTask;
476 if (oldTask != null) {
477 if (oldTask.isLaunching()) {
478 // don't downgrade isLaunching if we're already running
479 isLaunching = true;
Joe Onorato64e6be72010-03-05 15:05:52 -0500480 }
Joe Onorato36115782010-06-17 13:28:48 -0400481 oldTask.stopLocked();
Joe Onorato64e6be72010-03-05 15:05:52 -0500482 }
Joe Onorato36115782010-06-17 13:28:48 -0400483 mLoaderTask = new LoaderTask(context, isLaunching);
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700484 sWorker.post(mLoaderTask);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400485 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400486 }
487 }
488
Joe Onorato36115782010-06-17 13:28:48 -0400489 public void stopLoader() {
490 synchronized (mLock) {
491 if (mLoaderTask != null) {
492 mLoaderTask.stopLocked();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400493 }
494 }
Joe Onorato36115782010-06-17 13:28:48 -0400495 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400496
Joe Onorato36115782010-06-17 13:28:48 -0400497 /**
498 * Runnable for the thread that loads the contents of the launcher:
499 * - workspace icons
500 * - widgets
501 * - all apps icons
502 */
503 private class LoaderTask implements Runnable {
504 private Context mContext;
505 private Thread mWaitThread;
506 private boolean mIsLaunching;
507 private boolean mStopped;
508 private boolean mLoadAndBindStepFinished;
509
510 LoaderTask(Context context, boolean isLaunching) {
511 mContext = context;
512 mIsLaunching = isLaunching;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400513 }
514
Joe Onorato36115782010-06-17 13:28:48 -0400515 boolean isLaunching() {
516 return mIsLaunching;
517 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400518
Joe Onorato36115782010-06-17 13:28:48 -0400519 private void loadAndBindWorkspace() {
520 // Load the workspace
521
522 // For now, just always reload the workspace. It's ~100 ms vs. the
523 // binding which takes many hundreds of ms.
524 // We can reconsider.
525 if (DEBUG_LOADERS) {
526 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400527 }
Joe Onorato36115782010-06-17 13:28:48 -0400528 if (true || !mWorkspaceLoaded) {
529 loadWorkspace();
530 if (mStopped) {
531 return;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400532 }
Joe Onorato36115782010-06-17 13:28:48 -0400533 mWorkspaceLoaded = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400534 }
535
Joe Onorato36115782010-06-17 13:28:48 -0400536 // Bind the workspace
537 bindWorkspace();
538 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400539
Joe Onorato36115782010-06-17 13:28:48 -0400540 private void waitForIdle() {
541 // Wait until the either we're stopped or the other threads are done.
542 // This way we don't start loading all apps until the workspace has settled
543 // down.
544 synchronized (LoaderTask.this) {
545 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onoratocc67f472010-06-08 10:54:30 -0700546
Joe Onorato36115782010-06-17 13:28:48 -0400547 mHandler.postIdle(new Runnable() {
548 public void run() {
549 synchronized (LoaderTask.this) {
550 mLoadAndBindStepFinished = true;
551 if (DEBUG_LOADERS) {
552 Log.d(TAG, "done with previous binding step");
Daniel Sandler843e8602010-06-07 14:59:01 -0400553 }
Joe Onorato36115782010-06-17 13:28:48 -0400554 LoaderTask.this.notify();
Daniel Sandler843e8602010-06-07 14:59:01 -0400555 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400556 }
Joe Onorato36115782010-06-17 13:28:48 -0400557 });
558
559 while (!mStopped && !mLoadAndBindStepFinished) {
560 try {
561 this.wait();
562 } catch (InterruptedException ex) {
563 // Ignore
Daniel Sandler843e8602010-06-07 14:59:01 -0400564 }
565 }
Joe Onorato36115782010-06-17 13:28:48 -0400566 if (DEBUG_LOADERS) {
567 Log.d(TAG, "waited "
Winson Chungaafa03c2010-06-11 17:34:16 -0700568 + (SystemClock.uptimeMillis()-workspaceWaitTime)
Joe Onorato36115782010-06-17 13:28:48 -0400569 + "ms for previous step to finish binding");
570 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400571 }
Joe Onorato36115782010-06-17 13:28:48 -0400572 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400573
Joe Onorato36115782010-06-17 13:28:48 -0400574 public void run() {
575 // Optimize for end-user experience: if the Launcher is up and // running with the
576 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
577 // workspace first (default).
578 final Callbacks cbk = mCallbacks.get();
579 final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
Daniel Sandler843e8602010-06-07 14:59:01 -0400580
Joe Onorato36115782010-06-17 13:28:48 -0400581 keep_running: {
Daniel Sandler843e8602010-06-07 14:59:01 -0400582 // Elevate priority when Home launches for the first time to avoid
583 // starving at boot time. Staring at a blank home is not cool.
584 synchronized (mLock) {
585 android.os.Process.setThreadPriority(mIsLaunching
586 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
587 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400588 if (loadWorkspaceFirst) {
589 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
590 loadAndBindWorkspace();
591 } else {
592 if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700593 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400594 }
595
Joe Onorato36115782010-06-17 13:28:48 -0400596 if (mStopped) {
597 break keep_running;
598 }
599
600 // Whew! Hard work done. Slow us down, and wait until the UI thread has
601 // settled down.
Daniel Sandler843e8602010-06-07 14:59:01 -0400602 synchronized (mLock) {
603 if (mIsLaunching) {
604 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
605 }
606 }
Joe Onorato36115782010-06-17 13:28:48 -0400607 waitForIdle();
Daniel Sandler843e8602010-06-07 14:59:01 -0400608
609 // second step
610 if (loadWorkspaceFirst) {
611 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700612 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400613 } else {
614 if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
615 loadAndBindWorkspace();
616 }
Joe Onorato36115782010-06-17 13:28:48 -0400617 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400618
Joe Onorato36115782010-06-17 13:28:48 -0400619 // Clear out this reference, otherwise we end up holding it until all of the
620 // callback runnables are done.
621 mContext = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400622
Joe Onorato36115782010-06-17 13:28:48 -0400623 synchronized (mLock) {
624 // If we are still the last one to be scheduled, remove ourselves.
625 if (mLoaderTask == this) {
626 mLoaderTask = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400627 }
Joe Onorato36115782010-06-17 13:28:48 -0400628 }
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700629
Joe Onorato36115782010-06-17 13:28:48 -0400630 // Trigger a gc to try to clean up after the stuff is done, since the
631 // renderscript allocations aren't charged to the java heap.
632 if (mStopped) {
Joe Onoratof3d5ea92010-05-18 18:43:51 -0700633 mHandler.post(new Runnable() {
634 public void run() {
635 System.gc();
636 }
637 });
Joe Onorato36115782010-06-17 13:28:48 -0400638 } else {
639 mHandler.postIdle(new Runnable() {
640 public void run() {
641 System.gc();
Daniel Sandler8802e962010-05-26 16:28:16 -0400642 }
Joe Onorato36115782010-06-17 13:28:48 -0400643 });
644 }
645 }
646
647 public void stopLocked() {
648 synchronized (LoaderTask.this) {
649 mStopped = true;
650 this.notify();
651 }
652 }
653
654 /**
655 * Gets the callbacks object. If we've been stopped, or if the launcher object
656 * has somehow been garbage collected, return null instead. Pass in the Callbacks
657 * object that was around when the deferred message was scheduled, and if there's
658 * a new Callbacks object around then also return null. This will save us from
659 * calling onto it with data that will be ignored.
660 */
661 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
662 synchronized (mLock) {
663 if (mStopped) {
664 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -0400665 }
Joe Onorato36115782010-06-17 13:28:48 -0400666
667 if (mCallbacks == null) {
668 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -0400669 }
Joe Onorato36115782010-06-17 13:28:48 -0400670
671 final Callbacks callbacks = mCallbacks.get();
672 if (callbacks != oldCallbacks) {
673 return null;
674 }
675 if (callbacks == null) {
676 Log.w(TAG, "no mCallbacks");
677 return null;
678 }
679
680 return callbacks;
681 }
682 }
683
684 // check & update map of what's occupied; used to discard overlapping/invalid items
685 private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
686 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Daniel Sandler8802e962010-05-26 16:28:16 -0400687 return true;
688 }
Joe Onorato36115782010-06-17 13:28:48 -0400689 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
690 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
691 if (occupied[item.screen][x][y] != null) {
692 Log.e(TAG, "Error loading shortcut " + item
Winson Chungaafa03c2010-06-11 17:34:16 -0700693 + " into cell (" + item.screen + ":"
Joe Onorato36115782010-06-17 13:28:48 -0400694 + x + "," + y
Winson Chungaafa03c2010-06-11 17:34:16 -0700695 + ") occupied by "
Joe Onorato36115782010-06-17 13:28:48 -0400696 + occupied[item.screen][x][y]);
697 return false;
698 }
699 }
700 }
701 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
702 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
703 occupied[item.screen][x][y] = item;
704 }
705 }
706 return true;
707 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400708
Joe Onorato36115782010-06-17 13:28:48 -0400709 private void loadWorkspace() {
710 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400711
Joe Onorato36115782010-06-17 13:28:48 -0400712 final Context context = mContext;
713 final ContentResolver contentResolver = context.getContentResolver();
714 final PackageManager manager = context.getPackageManager();
715 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
716 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400717
Joe Onorato36115782010-06-17 13:28:48 -0400718 mItems.clear();
719 mAppWidgets.clear();
720 mFolders.clear();
Romain Guy5c16f3e2010-01-12 17:24:58 -0800721
Joe Onorato36115782010-06-17 13:28:48 -0400722 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400723
Joe Onorato36115782010-06-17 13:28:48 -0400724 final Cursor c = contentResolver.query(
725 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
Daniel Sandler8802e962010-05-26 16:28:16 -0400726
Adam Cohend22015c2010-07-26 22:02:18 -0700727 final ItemInfo occupied[][][] =
728 new ItemInfo[Launcher.SCREEN_COUNT][mCellCountX][mCellCountY];
Joe Onorato9c1289c2009-08-17 11:03:03 -0400729
Joe Onorato36115782010-06-17 13:28:48 -0400730 try {
731 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
732 final int intentIndex = c.getColumnIndexOrThrow
733 (LauncherSettings.Favorites.INTENT);
734 final int titleIndex = c.getColumnIndexOrThrow
735 (LauncherSettings.Favorites.TITLE);
736 final int iconTypeIndex = c.getColumnIndexOrThrow(
737 LauncherSettings.Favorites.ICON_TYPE);
738 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
739 final int iconPackageIndex = c.getColumnIndexOrThrow(
740 LauncherSettings.Favorites.ICON_PACKAGE);
741 final int iconResourceIndex = c.getColumnIndexOrThrow(
742 LauncherSettings.Favorites.ICON_RESOURCE);
743 final int containerIndex = c.getColumnIndexOrThrow(
744 LauncherSettings.Favorites.CONTAINER);
745 final int itemTypeIndex = c.getColumnIndexOrThrow(
746 LauncherSettings.Favorites.ITEM_TYPE);
747 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
748 LauncherSettings.Favorites.APPWIDGET_ID);
749 final int screenIndex = c.getColumnIndexOrThrow(
750 LauncherSettings.Favorites.SCREEN);
751 final int cellXIndex = c.getColumnIndexOrThrow
752 (LauncherSettings.Favorites.CELLX);
753 final int cellYIndex = c.getColumnIndexOrThrow
754 (LauncherSettings.Favorites.CELLY);
755 final int spanXIndex = c.getColumnIndexOrThrow
756 (LauncherSettings.Favorites.SPANX);
757 final int spanYIndex = c.getColumnIndexOrThrow(
758 LauncherSettings.Favorites.SPANY);
759 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
760 final int displayModeIndex = c.getColumnIndexOrThrow(
761 LauncherSettings.Favorites.DISPLAY_MODE);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400762
Joe Onorato36115782010-06-17 13:28:48 -0400763 ShortcutInfo info;
764 String intentDescription;
765 LauncherAppWidgetInfo appWidgetInfo;
766 int container;
767 long id;
768 Intent intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400769
Joe Onorato36115782010-06-17 13:28:48 -0400770 while (!mStopped && c.moveToNext()) {
771 try {
772 int itemType = c.getInt(itemTypeIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400773
Joe Onorato36115782010-06-17 13:28:48 -0400774 switch (itemType) {
775 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
776 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
777 intentDescription = c.getString(intentIndex);
778 try {
779 intent = Intent.parseUri(intentDescription, 0);
780 } catch (URISyntaxException e) {
781 continue;
782 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400783
Joe Onorato36115782010-06-17 13:28:48 -0400784 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
785 info = getShortcutInfo(manager, intent, context, c, iconIndex,
786 titleIndex);
787 } else {
788 info = getShortcutInfo(c, context, iconTypeIndex,
789 iconPackageIndex, iconResourceIndex, iconIndex,
790 titleIndex);
791 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400792
Joe Onorato36115782010-06-17 13:28:48 -0400793 if (info != null) {
794 updateSavedIcon(context, info, c, iconIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400795
Joe Onorato36115782010-06-17 13:28:48 -0400796 info.intent = intent;
797 info.id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400798 container = c.getInt(containerIndex);
Joe Onorato36115782010-06-17 13:28:48 -0400799 info.container = container;
800 info.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700801 info.cellX = c.getInt(cellXIndex);
802 info.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400803
Daniel Sandler8802e962010-05-26 16:28:16 -0400804 // check & update map of what's occupied
Joe Onorato36115782010-06-17 13:28:48 -0400805 if (!checkItemPlacement(occupied, info)) {
Daniel Sandler8802e962010-05-26 16:28:16 -0400806 break;
807 }
808
Joe Onorato9c1289c2009-08-17 11:03:03 -0400809 switch (container) {
Joe Onorato36115782010-06-17 13:28:48 -0400810 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
811 mItems.add(info);
812 break;
813 default:
814 // Item is in a user folder
815 UserFolderInfo folderInfo =
816 findOrMakeUserFolder(mFolders, container);
817 folderInfo.add(info);
818 break;
819 }
820 } else {
821 // Failed to load the shortcut, probably because the
822 // activity manager couldn't resolve it (maybe the app
823 // was uninstalled), or the db row was somehow screwed up.
824 // Delete it.
825 id = c.getLong(idIndex);
826 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
827 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
828 id, false), null, null);
829 }
830 break;
831
832 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
833 id = c.getLong(idIndex);
834 UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
835
Winson Chungaafa03c2010-06-11 17:34:16 -0700836 folderInfo.title = c.getString(titleIndex);
Joe Onorato36115782010-06-17 13:28:48 -0400837 folderInfo.id = id;
838 container = c.getInt(containerIndex);
839 folderInfo.container = container;
840 folderInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700841 folderInfo.cellX = c.getInt(cellXIndex);
842 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato36115782010-06-17 13:28:48 -0400843
844 // check & update map of what's occupied
845 if (!checkItemPlacement(occupied, folderInfo)) {
846 break;
847 }
Joe Onorato36115782010-06-17 13:28:48 -0400848 switch (container) {
849 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
850 mItems.add(folderInfo);
851 break;
852 }
853
854 mFolders.put(folderInfo.id, folderInfo);
855 break;
856
857 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
858 id = c.getLong(idIndex);
859 Uri uri = Uri.parse(c.getString(uriIndex));
860
861 // Make sure the live folder exists
862 final ProviderInfo providerInfo =
863 context.getPackageManager().resolveContentProvider(
864 uri.getAuthority(), 0);
865
866 if (providerInfo == null && !isSafeMode) {
867 itemsToRemove.add(id);
868 } else {
869 LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
Joe Onorato36115782010-06-17 13:28:48 -0400870 intentDescription = c.getString(intentIndex);
871 intent = null;
872 if (intentDescription != null) {
873 try {
874 intent = Intent.parseUri(intentDescription, 0);
875 } catch (URISyntaxException e) {
876 // Ignore, a live folder might not have a base intent
877 }
878 }
879
880 liveFolderInfo.title = c.getString(titleIndex);
881 liveFolderInfo.id = id;
882 liveFolderInfo.uri = uri;
883 container = c.getInt(containerIndex);
884 liveFolderInfo.container = container;
885 liveFolderInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700886 liveFolderInfo.cellX = c.getInt(cellXIndex);
887 liveFolderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato36115782010-06-17 13:28:48 -0400888 liveFolderInfo.baseIntent = intent;
889 liveFolderInfo.displayMode = c.getInt(displayModeIndex);
890
891 // check & update map of what's occupied
892 if (!checkItemPlacement(occupied, liveFolderInfo)) {
893 break;
894 }
895
896 loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
897 iconResourceIndex, liveFolderInfo);
898
899 switch (container) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400900 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
Joe Onorato36115782010-06-17 13:28:48 -0400901 mItems.add(liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400902 break;
903 }
Joe Onorato36115782010-06-17 13:28:48 -0400904 mFolders.put(liveFolderInfo.id, liveFolderInfo);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400905 }
Joe Onorato36115782010-06-17 13:28:48 -0400906 break;
Romain Guy5c16f3e2010-01-12 17:24:58 -0800907
Joe Onorato36115782010-06-17 13:28:48 -0400908 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
909 // Read all Launcher-specific widget details
910 int appWidgetId = c.getInt(appWidgetIdIndex);
911 id = c.getLong(idIndex);
912
913 final AppWidgetProviderInfo provider =
914 widgets.getAppWidgetInfo(appWidgetId);
Winson Chungaafa03c2010-06-11 17:34:16 -0700915
Joe Onorato36115782010-06-17 13:28:48 -0400916 if (!isSafeMode && (provider == null || provider.provider == null ||
917 provider.provider.getPackageName() == null)) {
918 Log.e(TAG, "Deleting widget that isn't installed anymore: id="
919 + id + " appWidgetId=" + appWidgetId);
920 itemsToRemove.add(id);
921 } else {
922 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
923 appWidgetInfo.id = id;
924 appWidgetInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700925 appWidgetInfo.cellX = c.getInt(cellXIndex);
926 appWidgetInfo.cellY = c.getInt(cellYIndex);
927 appWidgetInfo.spanX = c.getInt(spanXIndex);
928 appWidgetInfo.spanY = c.getInt(spanYIndex);
Joe Onorato36115782010-06-17 13:28:48 -0400929
930 container = c.getInt(containerIndex);
931 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
932 Log.e(TAG, "Widget found where container "
933 + "!= CONTAINER_DESKTOP -- ignoring!");
934 continue;
935 }
936 appWidgetInfo.container = c.getInt(containerIndex);
937
938 // check & update map of what's occupied
939 if (!checkItemPlacement(occupied, appWidgetInfo)) {
940 break;
941 }
942
943 mAppWidgets.add(appWidgetInfo);
944 }
945 break;
Romain Guy5c16f3e2010-01-12 17:24:58 -0800946 }
Joe Onorato36115782010-06-17 13:28:48 -0400947 } catch (Exception e) {
948 Log.w(TAG, "Desktop items loading interrupted:", e);
Romain Guy5c16f3e2010-01-12 17:24:58 -0800949 }
950 }
Joe Onorato36115782010-06-17 13:28:48 -0400951 } finally {
952 c.close();
953 }
Romain Guy5c16f3e2010-01-12 17:24:58 -0800954
Joe Onorato36115782010-06-17 13:28:48 -0400955 if (itemsToRemove.size() > 0) {
956 ContentProviderClient client = contentResolver.acquireContentProviderClient(
957 LauncherSettings.Favorites.CONTENT_URI);
958 // Remove dead items
959 for (long id : itemsToRemove) {
960 if (DEBUG_LOADERS) {
961 Log.d(TAG, "Removed id = " + id);
962 }
963 // Don't notify content observers
964 try {
965 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
966 null, null);
967 } catch (RemoteException e) {
968 Log.w(TAG, "Could not remove id = " + id);
Daniel Sandler8802e962010-05-26 16:28:16 -0400969 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800970 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400971 }
972
Joe Onorato36115782010-06-17 13:28:48 -0400973 if (DEBUG_LOADERS) {
974 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
975 Log.d(TAG, "workspace layout: ");
Adam Cohend22015c2010-07-26 22:02:18 -0700976 for (int y = 0; y < mCellCountY; y++) {
Joe Onorato36115782010-06-17 13:28:48 -0400977 String line = "";
978 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
979 if (s > 0) {
980 line += " | ";
981 }
Adam Cohend22015c2010-07-26 22:02:18 -0700982 for (int x = 0; x < mCellCountX; x++) {
Joe Onorato36115782010-06-17 13:28:48 -0400983 line += ((occupied[s][x][y] != null) ? "#" : ".");
984 }
985 }
986 Log.d(TAG, "[ " + line + " ]");
Joe Onorato9c1289c2009-08-17 11:03:03 -0400987 }
Joe Onorato36115782010-06-17 13:28:48 -0400988 }
989 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400990
Joe Onorato36115782010-06-17 13:28:48 -0400991 /**
992 * Read everything out of our database.
993 */
994 private void bindWorkspace() {
995 final long t = SystemClock.uptimeMillis();
996
997 // Don't use these two variables in any of the callback runnables.
998 // Otherwise we hold a reference to them.
999 final Callbacks oldCallbacks = mCallbacks.get();
1000 if (oldCallbacks == null) {
1001 // This launcher has exited and nobody bothered to tell us. Just bail.
1002 Log.w(TAG, "LoaderTask running with no launcher");
1003 return;
1004 }
1005
1006 int N;
1007 // Tell the workspace that we're about to start firing items at it
1008 mHandler.post(new Runnable() {
1009 public void run() {
1010 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1011 if (callbacks != null) {
1012 callbacks.startBinding();
1013 }
1014 }
1015 });
1016 // Add the items to the workspace.
1017 N = mItems.size();
1018 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1019 final int start = i;
1020 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001021 mHandler.post(new Runnable() {
1022 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001023 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001024 if (callbacks != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001025 callbacks.bindItems(mItems, start, start+chunkSize);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001026 }
1027 }
1028 });
Joe Onorato36115782010-06-17 13:28:48 -04001029 }
1030 mHandler.post(new Runnable() {
1031 public void run() {
1032 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1033 if (callbacks != null) {
1034 callbacks.bindFolders(mFolders);
1035 }
1036 }
1037 });
1038 // Wait until the queue goes empty.
1039 mHandler.post(new Runnable() {
1040 public void run() {
1041 if (DEBUG_LOADERS) {
1042 Log.d(TAG, "Going to start binding widgets soon.");
1043 }
1044 }
1045 });
1046 // Bind the widgets, one at a time.
1047 // WARNING: this is calling into the workspace from the background thread,
1048 // but since getCurrentScreen() just returns the int, we should be okay. This
1049 // is just a hint for the order, and if it's wrong, we'll be okay.
1050 // TODO: instead, we should have that push the current screen into here.
1051 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
1052 N = mAppWidgets.size();
1053 // once for the current screen
1054 for (int i=0; i<N; i++) {
1055 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1056 if (widget.screen == currentScreen) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001057 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) {
Joe Onorato36115782010-06-17 13:28:48 -04001061 callbacks.bindAppWidget(widget);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001062 }
1063 }
1064 });
1065 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001066 }
Joe Onorato36115782010-06-17 13:28:48 -04001067 // once for the other screens
1068 for (int i=0; i<N; i++) {
1069 final LauncherAppWidgetInfo widget = mAppWidgets.get(i);
1070 if (widget.screen != currentScreen) {
1071 mHandler.post(new Runnable() {
1072 public void run() {
1073 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1074 if (callbacks != null) {
1075 callbacks.bindAppWidget(widget);
1076 }
1077 }
1078 });
Joe Onoratocc67f472010-06-08 10:54:30 -07001079 }
1080 }
Joe Onorato36115782010-06-17 13:28:48 -04001081 // Tell the workspace that we're done.
1082 mHandler.post(new Runnable() {
1083 public void run() {
1084 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1085 if (callbacks != null) {
1086 callbacks.finishBindingItems();
1087 }
1088 }
1089 });
1090 // If we're profiling, this is the last thing in the queue.
1091 mHandler.post(new Runnable() {
1092 public void run() {
1093 if (DEBUG_LOADERS) {
1094 Log.d(TAG, "bound workspace in "
1095 + (SystemClock.uptimeMillis()-t) + "ms");
1096 }
1097 }
1098 });
1099 }
Joe Onoratocc67f472010-06-08 10:54:30 -07001100
Joe Onorato36115782010-06-17 13:28:48 -04001101 private void loadAndBindAllApps() {
1102 if (DEBUG_LOADERS) {
1103 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
1104 }
1105 if (!mAllAppsLoaded) {
1106 loadAllAppsByBatch();
1107 if (mStopped) {
Joe Onoratocc67f472010-06-08 10:54:30 -07001108 return;
1109 }
Joe Onorato36115782010-06-17 13:28:48 -04001110 mAllAppsLoaded = true;
1111 } else {
1112 onlyBindAllApps();
1113 }
1114 }
Joe Onoratocc67f472010-06-08 10:54:30 -07001115
Joe Onorato36115782010-06-17 13:28:48 -04001116 private void onlyBindAllApps() {
1117 final Callbacks oldCallbacks = mCallbacks.get();
1118 if (oldCallbacks == null) {
1119 // This launcher has exited and nobody bothered to tell us. Just bail.
1120 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
1121 return;
1122 }
1123
1124 // shallow copy
1125 final ArrayList<ApplicationInfo> list
1126 = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
1127 mHandler.post(new Runnable() {
1128 public void run() {
1129 final long t = SystemClock.uptimeMillis();
1130 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1131 if (callbacks != null) {
1132 callbacks.bindAllApplications(list);
1133 }
1134 if (DEBUG_LOADERS) {
1135 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
1136 + (SystemClock.uptimeMillis()-t) + "ms");
1137 }
1138 }
1139 });
1140
1141 }
1142
1143 private void loadAllAppsByBatch() {
1144 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1145
1146 // Don't use these two variables in any of the callback runnables.
1147 // Otherwise we hold a reference to them.
1148 final Callbacks oldCallbacks = mCallbacks.get();
1149 if (oldCallbacks == null) {
1150 // This launcher has exited and nobody bothered to tell us. Just bail.
1151 Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)");
1152 return;
1153 }
1154
1155 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1156 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1157
1158 final PackageManager packageManager = mContext.getPackageManager();
1159 List<ResolveInfo> apps = null;
1160
1161 int N = Integer.MAX_VALUE;
1162
1163 int startIndex;
1164 int i=0;
1165 int batchSize = -1;
1166 while (i < N && !mStopped) {
1167 if (i == 0) {
1168 mAllAppsList.clear();
1169 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1170 apps = packageManager.queryIntentActivities(mainIntent, 0);
1171 if (DEBUG_LOADERS) {
1172 Log.d(TAG, "queryIntentActivities took "
1173 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1174 }
1175 if (apps == null) {
1176 return;
1177 }
1178 N = apps.size();
1179 if (DEBUG_LOADERS) {
1180 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1181 }
1182 if (N == 0) {
1183 // There are no apps?!?
1184 return;
1185 }
1186 if (mBatchSize == 0) {
1187 batchSize = N;
1188 } else {
1189 batchSize = mBatchSize;
1190 }
1191
1192 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1193 Collections.sort(apps,
1194 new ResolveInfo.DisplayNameComparator(packageManager));
1195 if (DEBUG_LOADERS) {
1196 Log.d(TAG, "sort took "
1197 + (SystemClock.uptimeMillis()-sortTime) + "ms");
1198 }
1199 }
1200
1201 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1202
1203 startIndex = i;
1204 for (int j=0; i<N && j<batchSize; j++) {
1205 // This builds the icon bitmaps.
Patrick Dubroy3d605d52010-07-29 13:59:29 -07001206 mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), mIconCache));
Joe Onorato36115782010-06-17 13:28:48 -04001207 i++;
1208 }
1209
1210 final boolean first = i <= batchSize;
1211 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1212 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1213 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1214
Joe Onoratocc67f472010-06-08 10:54:30 -07001215 mHandler.post(new Runnable() {
1216 public void run() {
1217 final long t = SystemClock.uptimeMillis();
Joe Onoratocc67f472010-06-08 10:54:30 -07001218 if (callbacks != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001219 if (first) {
1220 callbacks.bindAllApplications(added);
1221 } else {
1222 callbacks.bindAppsAdded(added);
1223 }
1224 if (DEBUG_LOADERS) {
1225 Log.d(TAG, "bound " + added.size() + " apps in "
1226 + (SystemClock.uptimeMillis() - t) + "ms");
1227 }
1228 } else {
1229 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratocc67f472010-06-08 10:54:30 -07001230 }
1231 }
1232 });
1233
Daniel Sandlerdca66122010-04-13 16:23:58 -04001234 if (DEBUG_LOADERS) {
Joe Onorato36115782010-06-17 13:28:48 -04001235 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
1236 + (SystemClock.uptimeMillis()-t2) + "ms");
1237 }
1238
1239 if (mAllAppsLoadDelay > 0 && i < N) {
1240 try {
1241 if (DEBUG_LOADERS) {
1242 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1243 }
1244 Thread.sleep(mAllAppsLoadDelay);
1245 } catch (InterruptedException exc) { }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001246 }
1247 }
1248
Joe Onorato36115782010-06-17 13:28:48 -04001249 if (DEBUG_LOADERS) {
1250 Log.d(TAG, "cached all " + N + " apps in "
1251 + (SystemClock.uptimeMillis()-t) + "ms"
1252 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onoratobe386092009-11-17 17:32:16 -08001253 }
1254 }
1255
1256 public void dumpState() {
Joe Onorato36115782010-06-17 13:28:48 -04001257 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
1258 Log.d(TAG, "mLoaderTask.mWaitThread=" + mWaitThread);
1259 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
1260 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
1261 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
1262 }
1263 }
1264
1265 void enqueuePackageUpdated(PackageUpdatedTask task) {
Brad Fitzpatrick700889f2010-10-11 09:40:44 -07001266 sWorker.post(task);
Joe Onorato36115782010-06-17 13:28:48 -04001267 }
1268
1269 private class PackageUpdatedTask implements Runnable {
1270 int mOp;
1271 String[] mPackages;
1272
1273 public static final int OP_NONE = 0;
1274 public static final int OP_ADD = 1;
1275 public static final int OP_UPDATE = 2;
1276 public static final int OP_REMOVE = 3; // uninstlled
1277 public static final int OP_UNAVAILABLE = 4; // external media unmounted
1278
1279
1280 public PackageUpdatedTask(int op, String[] packages) {
1281 mOp = op;
1282 mPackages = packages;
1283 }
1284
1285 public void run() {
1286 final Context context = mApp;
1287
1288 final String[] packages = mPackages;
1289 final int N = packages.length;
1290 switch (mOp) {
1291 case OP_ADD:
1292 for (int i=0; i<N; i++) {
1293 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
1294 mAllAppsList.addPackage(context, packages[i]);
1295 }
1296 break;
1297 case OP_UPDATE:
1298 for (int i=0; i<N; i++) {
1299 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
1300 mAllAppsList.updatePackage(context, packages[i]);
1301 }
1302 break;
1303 case OP_REMOVE:
1304 case OP_UNAVAILABLE:
1305 for (int i=0; i<N; i++) {
1306 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
1307 mAllAppsList.removePackage(packages[i]);
1308 }
1309 break;
1310 }
1311
1312 ArrayList<ApplicationInfo> added = null;
1313 ArrayList<ApplicationInfo> removed = null;
1314 ArrayList<ApplicationInfo> modified = null;
1315
1316 if (mAllAppsList.added.size() > 0) {
1317 added = mAllAppsList.added;
1318 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1319 }
1320 if (mAllAppsList.removed.size() > 0) {
1321 removed = mAllAppsList.removed;
1322 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
1323 for (ApplicationInfo info: removed) {
1324 mIconCache.remove(info.intent.getComponent());
1325 }
1326 }
1327 if (mAllAppsList.modified.size() > 0) {
1328 modified = mAllAppsList.modified;
1329 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
1330 }
1331
1332 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
1333 if (callbacks == null) {
1334 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
1335 return;
1336 }
1337
1338 if (added != null) {
1339 final ArrayList<ApplicationInfo> addedFinal = added;
1340 mHandler.post(new Runnable() {
1341 public void run() {
1342 if (callbacks == mCallbacks.get()) {
1343 callbacks.bindAppsAdded(addedFinal);
1344 }
1345 }
1346 });
1347 }
1348 if (modified != null) {
1349 final ArrayList<ApplicationInfo> modifiedFinal = modified;
1350 mHandler.post(new Runnable() {
1351 public void run() {
1352 if (callbacks == mCallbacks.get()) {
1353 callbacks.bindAppsUpdated(modifiedFinal);
1354 }
1355 }
1356 });
1357 }
1358 if (removed != null) {
1359 final boolean permanent = mOp != OP_UNAVAILABLE;
1360 final ArrayList<ApplicationInfo> removedFinal = removed;
1361 mHandler.post(new Runnable() {
1362 public void run() {
1363 if (callbacks == mCallbacks.get()) {
1364 callbacks.bindAppsRemoved(removedFinal, permanent);
1365 }
1366 }
1367 });
Joe Onoratobe386092009-11-17 17:32:16 -08001368 }
Winson Chung80baf5a2010-08-09 16:03:15 -07001369
1370 mHandler.post(new Runnable() {
1371 @Override
1372 public void run() {
1373 if (callbacks == mCallbacks.get()) {
1374 callbacks.bindPackagesUpdated();
1375 }
1376 }
1377 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001378 }
1379 }
1380
1381 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001382 * This is called from the code that adds shortcuts from the intent receiver. This
1383 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001384 */
Joe Onorato56d82912010-03-07 14:32:10 -05001385 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Joe Onoratoe74daed2010-03-11 12:32:24 -08001386 return getShortcutInfo(manager, intent, context, null, -1, -1);
Joe Onorato56d82912010-03-07 14:32:10 -05001387 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001388
Joe Onorato56d82912010-03-07 14:32:10 -05001389 /**
1390 * Make an ShortcutInfo object for a shortcut that is an application.
1391 *
1392 * If c is not null, then it will be used to fill in missing data like the title and icon.
1393 */
1394 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
1395 Cursor c, int iconIndex, int titleIndex) {
1396 Bitmap icon = null;
1397 final ShortcutInfo info = new ShortcutInfo();
1398
1399 ComponentName componentName = intent.getComponent();
1400 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001401 return null;
1402 }
1403
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001404 // TODO: See if the PackageManager knows about this case. If it doesn't
1405 // then return null & delete this.
1406
Joe Onorato56d82912010-03-07 14:32:10 -05001407 // the resource -- This may implicitly give us back the fallback icon,
1408 // but don't worry about that. All we're doing with usingFallbackIcon is
1409 // to avoid saving lots of copies of that in the database, and most apps
1410 // have icons anyway.
1411 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1412 if (resolveInfo != null) {
1413 icon = mIconCache.getIcon(componentName, resolveInfo);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001414 }
Joe Onorato56d82912010-03-07 14:32:10 -05001415 // the db
1416 if (icon == null) {
1417 if (c != null) {
1418 icon = getIconFromCursor(c, iconIndex);
1419 }
1420 }
1421 // the fallback icon
1422 if (icon == null) {
1423 icon = getFallbackIcon();
1424 info.usingFallbackIcon = true;
1425 }
1426 info.setIcon(icon);
1427
1428 // from the resource
1429 if (resolveInfo != null) {
1430 info.title = resolveInfo.activityInfo.loadLabel(manager);
1431 }
1432 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001433 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001434 if (c != null) {
1435 info.title = c.getString(titleIndex);
1436 }
1437 }
1438 // fall back to the class name of the activity
1439 if (info.title == null) {
1440 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001441 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001442 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1443 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001444 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001445
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001446 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001447 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001448 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001449 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001450 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1451 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001452
Joe Onorato56d82912010-03-07 14:32:10 -05001453 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001454 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001455 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001456
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001457 // TODO: If there's an explicit component and we can't install that, delete it.
1458
Joe Onorato56d82912010-03-07 14:32:10 -05001459 info.title = c.getString(titleIndex);
1460
Joe Onorato9c1289c2009-08-17 11:03:03 -04001461 int iconType = c.getInt(iconTypeIndex);
1462 switch (iconType) {
1463 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1464 String packageName = c.getString(iconPackageIndex);
1465 String resourceName = c.getString(iconResourceIndex);
1466 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001467 info.customIcon = false;
1468 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001469 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001470 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001471 if (resources != null) {
1472 final int id = resources.getIdentifier(resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07001473 icon = Utilities.createIconBitmap(
1474 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato56d82912010-03-07 14:32:10 -05001475 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001476 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001477 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001478 }
Joe Onorato56d82912010-03-07 14:32:10 -05001479 // the db
1480 if (icon == null) {
1481 icon = getIconFromCursor(c, iconIndex);
1482 }
1483 // the fallback icon
1484 if (icon == null) {
1485 icon = getFallbackIcon();
1486 info.usingFallbackIcon = true;
1487 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001488 break;
1489 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Joe Onorato56d82912010-03-07 14:32:10 -05001490 icon = getIconFromCursor(c, iconIndex);
1491 if (icon == null) {
1492 icon = getFallbackIcon();
1493 info.customIcon = false;
1494 info.usingFallbackIcon = true;
1495 } else {
1496 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001497 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001498 break;
1499 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001500 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001501 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001502 info.customIcon = false;
1503 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001504 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001505 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001506 return info;
1507 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001508
Joe Onorato56d82912010-03-07 14:32:10 -05001509 Bitmap getIconFromCursor(Cursor c, int iconIndex) {
1510 if (false) {
1511 Log.d(TAG, "getIconFromCursor app="
1512 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1513 }
1514 byte[] data = c.getBlob(iconIndex);
1515 try {
1516 return BitmapFactory.decodeByteArray(data, 0, data.length);
1517 } catch (Exception e) {
1518 return null;
1519 }
1520 }
1521
Joe Onorato0589f0f2010-02-08 13:44:00 -08001522 ShortcutInfo addShortcut(Context context, Intent data,
Michael Jurka0280c3b2010-09-17 15:00:07 -07001523 int screen, int cellX, int cellY, boolean notify) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001524
Winson Chunga9abd0e2010-10-27 17:18:37 -07001525 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001526 addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
Michael Jurka0280c3b2010-09-17 15:00:07 -07001527 screen, cellX, cellY, notify);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001528
1529 return info;
1530 }
1531
Winson Chunga9abd0e2010-10-27 17:18:37 -07001532 /**
Winson Chung55cef262010-10-28 14:14:18 -07001533 * Attempts to find an AppWidgetProviderInfo that matches the given component.
1534 */
1535 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
1536 ComponentName component) {
1537 List<AppWidgetProviderInfo> widgets =
1538 AppWidgetManager.getInstance(context).getInstalledProviders();
1539 for (AppWidgetProviderInfo info : widgets) {
1540 if (info.provider.equals(component)) {
1541 return info;
1542 }
1543 }
1544 return null;
Winson Chunga9abd0e2010-10-27 17:18:37 -07001545 }
1546
Winson Chung68846fd2010-10-29 11:00:27 -07001547 /**
1548 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
1549 */
1550 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
1551 final PackageManager packageManager = context.getPackageManager();
1552 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
1553 new ArrayList<WidgetMimeTypeHandlerData>();
1554
1555 final Intent supportsIntent =
1556 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
1557 supportsIntent.setType(mimeType);
1558
1559 // Create a set of widget configuration components that we can test against
1560 final List<AppWidgetProviderInfo> widgets =
1561 AppWidgetManager.getInstance(context).getInstalledProviders();
1562 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
1563 new HashMap<ComponentName, AppWidgetProviderInfo>();
1564 for (AppWidgetProviderInfo info : widgets) {
1565 configurationComponentToWidget.put(info.configure, info);
1566 }
1567
1568 // Run through each of the intents that can handle this type of clip data, and cross
1569 // reference them with the components that are actual configuration components
1570 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
1571 PackageManager.MATCH_DEFAULT_ONLY);
1572 for (ResolveInfo info : activities) {
1573 final ActivityInfo activityInfo = info.activityInfo;
1574 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
1575 activityInfo.name);
1576 if (configurationComponentToWidget.containsKey(infoComponent)) {
1577 supportedConfigurationActivities.add(
1578 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
1579 configurationComponentToWidget.get(infoComponent)));
1580 }
1581 }
1582 return supportedConfigurationActivities;
1583 }
1584
Winson Chunga9abd0e2010-10-27 17:18:37 -07001585 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001586 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1587 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1588 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1589
1590 Bitmap icon = null;
1591 boolean filtered = false;
1592 boolean customIcon = false;
1593 ShortcutIconResource iconResource = null;
1594
1595 if (bitmap != null && bitmap instanceof Bitmap) {
1596 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
1597 filtered = true;
1598 customIcon = true;
1599 } else {
1600 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1601 if (extra != null && extra instanceof ShortcutIconResource) {
1602 try {
1603 iconResource = (ShortcutIconResource) extra;
1604 final PackageManager packageManager = context.getPackageManager();
1605 Resources resources = packageManager.getResourcesForApplication(
1606 iconResource.packageName);
1607 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07001608 icon = Utilities.createIconBitmap(
1609 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001610 } catch (Exception e) {
1611 Log.w(TAG, "Could not load shortcut icon: " + extra);
1612 }
1613 }
1614 }
1615
Joe Onorato0589f0f2010-02-08 13:44:00 -08001616 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001617
1618 if (icon == null) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07001619 if (fallbackIcon != null) {
1620 icon = fallbackIcon;
1621 } else {
1622 icon = getFallbackIcon();
1623 info.usingFallbackIcon = true;
1624 }
Joe Onorato56d82912010-03-07 14:32:10 -05001625 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001626 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001627
Joe Onorato0589f0f2010-02-08 13:44:00 -08001628 info.title = name;
1629 info.intent = intent;
1630 info.customIcon = customIcon;
1631 info.iconResource = iconResource;
1632
1633 return info;
1634 }
1635
Michael Jurkac9a96192010-11-01 11:52:08 -07001636 private void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001637 int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) {
1638
1639 int iconType = c.getInt(iconTypeIndex);
1640 switch (iconType) {
1641 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1642 String packageName = c.getString(iconPackageIndex);
1643 String resourceName = c.getString(iconResourceIndex);
1644 PackageManager packageManager = context.getPackageManager();
1645 try {
Michael Jurkac9a96192010-11-01 11:52:08 -07001646 Resources appResources = packageManager.getResourcesForApplication(packageName);
1647 final int id = appResources.getIdentifier(resourceName, null, null);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001648 liveFolderInfo.icon = Utilities.createIconBitmap(
Michael Jurkac9a96192010-11-01 11:52:08 -07001649 mIconCache.getFullResIcon(appResources, id), context);
1650 } catch (Exception e) {
1651 Resources resources = context.getResources();
1652 liveFolderInfo.icon = Utilities.createIconBitmap(
1653 mIconCache.getFullResIcon(resources, R.drawable.ic_launcher_folder),
Joe Onorato0589f0f2010-02-08 13:44:00 -08001654 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001655 }
1656 liveFolderInfo.iconResource = new Intent.ShortcutIconResource();
1657 liveFolderInfo.iconResource.packageName = packageName;
1658 liveFolderInfo.iconResource.resourceName = resourceName;
1659 break;
1660 default:
Michael Jurkac9a96192010-11-01 11:52:08 -07001661 Resources resources = context.getResources();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001662 liveFolderInfo.icon = Utilities.createIconBitmap(
Michael Jurkac9a96192010-11-01 11:52:08 -07001663 mIconCache.getFullResIcon(resources, R.drawable.ic_launcher_folder),
Joe Onorato0589f0f2010-02-08 13:44:00 -08001664 context);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001665 }
1666 }
1667
Joe Onorato56d82912010-03-07 14:32:10 -05001668 void updateSavedIcon(Context context, ShortcutInfo info, Cursor c, int iconIndex) {
1669 // If this icon doesn't have a custom icon, check to see
1670 // what's stored in the DB, and if it doesn't match what
1671 // we're going to show, store what we are going to show back
1672 // into the DB. We do this so when we're loading, if the
1673 // package manager can't find an icon (for example because
1674 // the app is on SD) then we can use that instead.
Joe Onoratoddc9c1f2010-08-30 18:30:15 -07001675 if (!info.customIcon && !info.usingFallbackIcon) {
Joe Onorato56d82912010-03-07 14:32:10 -05001676 boolean needSave;
1677 byte[] data = c.getBlob(iconIndex);
1678 try {
1679 if (data != null) {
1680 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1681 Bitmap loaded = info.getIcon(mIconCache);
1682 needSave = !saved.sameAs(loaded);
1683 } else {
1684 needSave = true;
1685 }
1686 } catch (Exception e) {
1687 needSave = true;
1688 }
1689 if (needSave) {
1690 Log.d(TAG, "going to save icon bitmap for info=" + info);
1691 // This is slower than is ideal, but this only happens either
1692 // after the froyo OTA or when the app is updated with a new
1693 // icon.
1694 updateItemInDatabase(context, info);
1695 }
1696 }
1697 }
1698
Joe Onorato9c1289c2009-08-17 11:03:03 -04001699 /**
1700 * Return an existing UserFolderInfo object if we have encountered this ID previously,
1701 * or make a new one.
1702 */
1703 private static UserFolderInfo findOrMakeUserFolder(HashMap<Long, FolderInfo> folders, long id) {
1704 // See if a placeholder was created for us already
1705 FolderInfo folderInfo = folders.get(id);
1706 if (folderInfo == null || !(folderInfo instanceof UserFolderInfo)) {
1707 // No placeholder -- create a new instance
1708 folderInfo = new UserFolderInfo();
1709 folders.put(id, folderInfo);
1710 }
1711 return (UserFolderInfo) folderInfo;
1712 }
1713
1714 /**
1715 * Return an existing UserFolderInfo object if we have encountered this ID previously, or make a
1716 * new one.
1717 */
1718 private static LiveFolderInfo findOrMakeLiveFolder(HashMap<Long, FolderInfo> folders, long id) {
1719 // See if a placeholder was created for us already
1720 FolderInfo folderInfo = folders.get(id);
1721 if (folderInfo == null || !(folderInfo instanceof LiveFolderInfo)) {
1722 // No placeholder -- create a new instance
1723 folderInfo = new LiveFolderInfo();
1724 folders.put(id, folderInfo);
1725 }
1726 return (LiveFolderInfo) folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001727 }
1728
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001729 private static String getLabel(PackageManager manager, ActivityInfo activityInfo) {
1730 String label = activityInfo.loadLabel(manager).toString();
1731 if (label == null) {
1732 label = manager.getApplicationLabel(activityInfo.applicationInfo).toString();
1733 if (label == null) {
1734 label = activityInfo.name;
1735 }
1736 }
1737 return label;
1738 }
1739
Joe Onorato9c1289c2009-08-17 11:03:03 -04001740 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001741 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001742 = new Comparator<ApplicationInfo>() {
1743 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1744 return sCollator.compare(a.title.toString(), b.title.toString());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001745 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001746 };
Joe Onoratobe386092009-11-17 17:32:16 -08001747
1748 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08001749 Log.d(TAG, "mCallbacks=" + mCallbacks);
1750 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
1751 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
1752 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
1753 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
Joe Onorato36115782010-06-17 13:28:48 -04001754 Log.d(TAG, "mItems size=" + mItems.size());
1755 if (mLoaderTask != null) {
1756 mLoaderTask.dumpState();
1757 } else {
1758 Log.d(TAG, "mLoaderTask=null");
1759 }
Joe Onoratobe386092009-11-17 17:32:16 -08001760 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001761}