blob: c06bc0c31fa2d12e1c18d13d5a6671a39f384c27 [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
Narayan Kamathcb1a4772011-06-28 13:46:59 +010019import android.app.SearchManager;
Romain Guy629de3e2010-01-13 12:20:59 -080020import android.appwidget.AppWidgetManager;
21import android.appwidget.AppWidgetProviderInfo;
Joe Onoratof99f8c12009-10-31 17:27:36 -040022import android.content.BroadcastReceiver;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.content.ComponentName;
Romain Guy5c16f3e2010-01-12 17:24:58 -080024import android.content.ContentProviderClient;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.ContentResolver;
26import android.content.ContentValues;
Winson Chungaafa03c2010-06-11 17:34:16 -070027import android.content.Context;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080029import android.content.Intent.ShortcutIconResource;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.pm.ActivityInfo;
31import android.content.pm.PackageManager;
32import android.content.pm.ResolveInfo;
Reena Lee93f824a2011-09-23 17:20:28 -070033import android.content.res.Configuration;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.content.res.Resources;
35import android.database.Cursor;
36import android.graphics.Bitmap;
37import android.graphics.BitmapFactory;
38import android.net.Uri;
Joe Onorato17a89222011-02-08 17:26:11 -080039import android.os.Environment;
Joe Onorato36115782010-06-17 13:28:48 -040040import android.os.Handler;
41import android.os.HandlerThread;
Joe Onorato0589f0f2010-02-08 13:44:00 -080042import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043import android.os.Process;
Winson Chungaafa03c2010-06-11 17:34:16 -070044import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040045import android.os.SystemClock;
Winson Chungaafa03c2010-06-11 17:34:16 -070046import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047
Winson Chung68846fd2010-10-29 11:00:27 -070048import com.android.launcher.R;
49import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
Romain Guyedcce092010-03-04 13:03:17 -080050
Michael Jurkac2f801e2011-07-12 14:19:46 -070051import java.lang.ref.WeakReference;
52import java.net.URISyntaxException;
53import java.text.Collator;
54import java.util.ArrayList;
55import java.util.Collections;
56import java.util.Comparator;
57import java.util.HashMap;
58import java.util.List;
59import java.util.Locale;
60
The Android Open Source Project31dd5032009-03-03 19:32:27 -080061/**
62 * Maintains in-memory state of the Launcher. It is expected that there should be only one
63 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070064 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040066public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080067 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040068 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070069
Joe Onorato36115782010-06-17 13:28:48 -040070 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
Joe Onorato17a89222011-02-08 17:26:11 -080071 private final boolean mAppsCanBeOnExternalStorage;
Joe Onoratod65d08e2010-04-20 15:43:37 -040072 private int mBatchSize; // 0 is all apps at once
Daniel Sandler2ff10b32010-04-16 15:06:06 -040073 private int mAllAppsLoadDelay; // milliseconds between batches
Daniel Sandlerdca66122010-04-13 16:23:58 -040074
Joe Onoratof99f8c12009-10-31 17:27:36 -040075 private final LauncherApplication mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040076 private final Object mLock = new Object();
77 private DeferredHandler mHandler = new DeferredHandler();
Joe Onorato36115782010-06-17 13:28:48 -040078 private LoaderTask mLoaderTask;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079
Brad Fitzpatrick700889f2010-10-11 09:40:44 -070080 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
81 static {
82 sWorkerThread.start();
83 }
84 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
85
Joe Onoratocc67f472010-06-08 10:54:30 -070086 // We start off with everything not loaded. After that, we assume that
87 // our monitoring of the package manager provides all updates and we never
88 // need to do a requery. These are only ever touched from the loader thread.
89 private boolean mWorkspaceLoaded;
90 private boolean mAllAppsLoaded;
91
Joe Onorato9c1289c2009-08-17 11:03:03 -040092 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080093
Michael Jurkaa8c760d2011-04-28 14:59:33 -070094 // < only access in worker thread >
95 private AllAppsList mAllAppsList;
Joe Onorato0589f0f2010-02-08 13:44:00 -080096
Michael Jurkaa8c760d2011-04-28 14:59:33 -070097 // sItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
98 // LauncherModel to their ids
99 static final HashMap<Long, ItemInfo> sItemsIdMap = new HashMap<Long, ItemInfo>();
100
101 // sItems is passed to bindItems, which expects a list of all folders and shortcuts created by
102 // LauncherModel that are directly on the home screen (however, no widgets or shortcuts
103 // within folders).
Adam Cohen4eac29a2011-07-11 17:53:37 -0700104 static final ArrayList<ItemInfo> sWorkspaceItems = new ArrayList<ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700105
106 // sAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
107 static final ArrayList<LauncherAppWidgetInfo> sAppWidgets =
108 new ArrayList<LauncherAppWidgetInfo>();
109
110 // sFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
111 static final HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
Winson Chungb1094bd2011-08-24 16:14:08 -0700112
113 // sDbIconCache is the set of ItemInfos that need to have their icons updated in the database
114 static final HashMap<Object, byte[]> sDbIconCache = new HashMap<Object, byte[]>();
115
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700116 // </ only access in worker thread >
117
118 private IconCache mIconCache;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800119 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120
Adam Cohend22015c2010-07-26 22:02:18 -0700121 private static int mCellCountX;
122 private static int mCellCountY;
Winson Chungaafa03c2010-06-11 17:34:16 -0700123
Reena Lee99a73f32011-10-24 17:27:37 -0700124 protected int mPreviousConfigMcc;
Reena Lee93f824a2011-09-23 17:20:28 -0700125
Joe Onorato9c1289c2009-08-17 11:03:03 -0400126 public interface Callbacks {
Joe Onoratoef2efcf2010-10-27 13:21:00 -0700127 public boolean setLoadOnResume();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400128 public int getCurrentWorkspaceScreen();
129 public void startBinding();
130 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
Joe Onoratoad72e172009-11-06 16:25:04 -0500131 public void bindFolders(HashMap<Long,FolderInfo> folders);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400132 public void finishBindingItems();
133 public void bindAppWidget(LauncherAppWidgetInfo info);
134 public void bindAllApplications(ArrayList<ApplicationInfo> apps);
Joe Onorato64e6be72010-03-05 15:05:52 -0500135 public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
136 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
Joe Onorato36115782010-06-17 13:28:48 -0400137 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
Winson Chung80baf5a2010-08-09 16:03:15 -0700138 public void bindPackagesUpdated();
Daniel Sandler843e8602010-06-07 14:59:01 -0400139 public boolean isAllAppsVisible();
Narayan Kamathcb1a4772011-06-28 13:46:59 +0100140 public void bindSearchablesChanged();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400141 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800142
Joe Onorato0589f0f2010-02-08 13:44:00 -0800143 LauncherModel(LauncherApplication app, IconCache iconCache) {
Joe Onorato17a89222011-02-08 17:26:11 -0800144 mAppsCanBeOnExternalStorage = !Environment.isExternalStorageEmulated();
Joe Onoratof99f8c12009-10-31 17:27:36 -0400145 mApp = app;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800146 mAllAppsList = new AllAppsList(iconCache);
147 mIconCache = iconCache;
148
149 mDefaultIcon = Utilities.createIconBitmap(
Michael Jurkac9a96192010-11-01 11:52:08 -0700150 mIconCache.getFullResDefaultActivityIcon(), app);
Daniel Sandler2ff10b32010-04-16 15:06:06 -0400151
Reena Lee93f824a2011-09-23 17:20:28 -0700152 final Resources res = app.getResources();
153 mAllAppsLoadDelay = res.getInteger(R.integer.config_allAppsBatchLoadDelay);
154 mBatchSize = res.getInteger(R.integer.config_allAppsBatchSize);
Reena Lee99a73f32011-10-24 17:27:37 -0700155 Configuration config = res.getConfiguration();
156 mPreviousConfigMcc = config.mcc;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800157 }
158
Joe Onorato56d82912010-03-07 14:32:10 -0500159 public Bitmap getFallbackIcon() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800160 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400161 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800162
Winson Chung603bcb92011-09-02 11:45:39 -0700163 public void unbindWorkspaceItems() {
164 sWorker.post(new Runnable() {
165 @Override
166 public void run() {
167 unbindWorkspaceItemsOnMainThread();
168 }
169 });
170 }
171
172 /** Unbinds all the sWorkspaceItems on the main thread, and return a copy of sWorkspaceItems
173 * that is save to reference from the main thread. */
174 private ArrayList<ItemInfo> unbindWorkspaceItemsOnMainThread() {
175 // Ensure that we don't use the same workspace items data structure on the main thread
176 // by making a copy of workspace items first.
177 final ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>(sWorkspaceItems);
178 mHandler.post(new Runnable() {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700179 @Override
Winson Chung603bcb92011-09-02 11:45:39 -0700180 public void run() {
181 for (ItemInfo item : workspaceItems) {
182 item.unbind();
183 }
184 }
185 });
186
187 return workspaceItems;
Adam Cohen4eac29a2011-07-11 17:53:37 -0700188 }
189
Joe Onorato9c1289c2009-08-17 11:03:03 -0400190 /**
191 * Adds an item to the DB if it was not created previously, or move it to a new
192 * <container, screen, cellX, cellY>
193 */
194 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
195 int screen, int cellX, int cellY) {
196 if (item.container == ItemInfo.NO_ID) {
197 // From all apps
198 addItemToDatabase(context, item, container, screen, cellX, cellY, false);
199 } else {
200 // From somewhere else
201 moveItemInDatabase(context, item, container, screen, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800202 }
203 }
204
Michael Jurkac9d95c52011-08-29 14:03:34 -0700205 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
206 final ItemInfo item, final String callingFunction) {
207 final long itemId = item.id;
208 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
209 final ContentResolver cr = context.getContentResolver();
210
211 Runnable r = new Runnable() {
212 public void run() {
213 cr.update(uri, values, null, null);
214
215 ItemInfo modelItem = sItemsIdMap.get(itemId);
216 if (item != modelItem) {
217 // the modelItem needs to match up perfectly with item if our model is to be
218 // consistent with the database-- for now, just require modelItem == item
219 String msg = "item: " + ((item != null) ? item.toString() : "null") +
220 "modelItem: " + ((modelItem != null) ? modelItem.toString() : "null") +
221 "Error: ItemInfo passed to " + callingFunction + " doesn't match original";
222 throw new RuntimeException(msg);
223 }
224
225 // Items are added/removed from the corresponding FolderInfo elsewhere, such
226 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
227 // that are on the desktop, as appropriate
228 if (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
229 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
230 if (!sWorkspaceItems.contains(modelItem)) {
231 sWorkspaceItems.add(modelItem);
232 }
233 } else {
234 sWorkspaceItems.remove(modelItem);
235 }
236 }
237 };
238
239 if (sWorkerThread.getThreadId() == Process.myTid()) {
240 r.run();
241 } else {
242 sWorker.post(r);
243 }
244 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800245 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400246 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700247 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700248 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
249 final int screen, final int cellX, final int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400250 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400251 item.cellX = cellX;
252 item.cellY = cellY;
Michael Jurkac9d95c52011-08-29 14:03:34 -0700253
Winson Chung3d503fb2011-07-13 17:25:49 -0700254 // We store hotseat items in canonical form which is this orientation invariant position
255 // in the hotseat
256 if (context instanceof Launcher && screen < 0 &&
257 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
258 item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
259 } else {
260 item.screen = screen;
261 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400262
263 final ContentValues values = new ContentValues();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400264 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Winson Chung3d503fb2011-07-13 17:25:49 -0700265 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
266 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400267 values.put(LauncherSettings.Favorites.SCREEN, item.screen);
268
Michael Jurkac9d95c52011-08-29 14:03:34 -0700269 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700270 }
271
272 /**
Adam Cohen1b607ed2011-03-03 17:26:50 -0800273 * Resize an item in the DB to a new <spanX, spanY, cellX, cellY>
Adam Cohend4844c32011-02-18 19:25:06 -0800274 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700275 static void resizeItemInDatabase(Context context, final ItemInfo item, final int cellX,
276 final int cellY, final int spanX, final int spanY) {
Adam Cohend4844c32011-02-18 19:25:06 -0800277 item.spanX = spanX;
278 item.spanY = spanY;
279 item.cellX = cellX;
280 item.cellY = cellY;
281
Adam Cohend4844c32011-02-18 19:25:06 -0800282 final ContentValues values = new ContentValues();
Adam Cohend4844c32011-02-18 19:25:06 -0800283 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
284 values.put(LauncherSettings.Favorites.SPANX, spanX);
285 values.put(LauncherSettings.Favorites.SPANY, spanY);
286 values.put(LauncherSettings.Favorites.CELLX, cellX);
287 values.put(LauncherSettings.Favorites.CELLY, cellY);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700288 updateItemInDatabaseHelper(context, values, item, "resizeItemInDatabase");
289 }
Adam Cohend4844c32011-02-18 19:25:06 -0800290
Michael Jurkac9d95c52011-08-29 14:03:34 -0700291
292 /**
293 * Update an item to the database in a specified container.
294 */
295 static void updateItemInDatabase(Context context, final ItemInfo item) {
296 final ContentValues values = new ContentValues();
297 item.onAddToDatabase(values);
298 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
299 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
Adam Cohend4844c32011-02-18 19:25:06 -0800300 }
301
302 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400303 * Returns true if the shortcuts already exists in the database.
304 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800305 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400306 static boolean shortcutExists(Context context, String title, Intent intent) {
307 final ContentResolver cr = context.getContentResolver();
308 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
309 new String[] { "title", "intent" }, "title=? and intent=?",
310 new String[] { title, intent.toUri(0) }, null);
311 boolean result = false;
312 try {
313 result = c.moveToFirst();
314 } finally {
315 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800316 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400317 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700318 }
319
Joe Onorato9c1289c2009-08-17 11:03:03 -0400320 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700321 * Returns an ItemInfo array containing all the items in the LauncherModel.
322 * The ItemInfo.id is not set through this function.
323 */
324 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
325 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
326 final ContentResolver cr = context.getContentResolver();
327 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
328 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
329 LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
330 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
331
332 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
333 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
334 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
335 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
336 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
337 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
338 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
339
340 try {
341 while (c.moveToNext()) {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700342 ItemInfo item = new ItemInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -0700343 item.cellX = c.getInt(cellXIndex);
344 item.cellY = c.getInt(cellYIndex);
345 item.spanX = c.getInt(spanXIndex);
346 item.spanY = c.getInt(spanYIndex);
347 item.container = c.getInt(containerIndex);
348 item.itemType = c.getInt(itemTypeIndex);
349 item.screen = c.getInt(screenIndex);
350
351 items.add(item);
352 }
353 } catch (Exception e) {
354 items.clear();
355 } finally {
356 c.close();
357 }
358
359 return items;
360 }
361
362 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400363 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
364 */
365 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
366 final ContentResolver cr = context.getContentResolver();
367 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
368 "_id=? and (itemType=? or itemType=?)",
369 new String[] { String.valueOf(id),
Adam Cohendf2cc412011-04-27 16:56:57 -0700370 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700371
Joe Onorato9c1289c2009-08-17 11:03:03 -0400372 try {
373 if (c.moveToFirst()) {
374 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
375 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
376 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
377 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
378 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
379 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800380
Joe Onorato9c1289c2009-08-17 11:03:03 -0400381 FolderInfo folderInfo = null;
382 switch (c.getInt(itemTypeIndex)) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700383 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
384 folderInfo = findOrMakeFolder(folderList, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400385 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700386 }
387
Joe Onorato9c1289c2009-08-17 11:03:03 -0400388 folderInfo.title = c.getString(titleIndex);
389 folderInfo.id = id;
390 folderInfo.container = c.getInt(containerIndex);
391 folderInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700392 folderInfo.cellX = c.getInt(cellXIndex);
393 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400394
395 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700396 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400397 } finally {
398 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700399 }
400
401 return null;
402 }
403
Joe Onorato9c1289c2009-08-17 11:03:03 -0400404 /**
405 * Add an item to the database in a specified container. Sets the container, screen, cellX and
406 * cellY fields of the item. Also assigns an ID to the item.
407 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700408 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
409 final int screen, final int cellX, final int cellY, final boolean notify) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400410 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400411 item.cellX = cellX;
412 item.cellY = cellY;
Winson Chung3d503fb2011-07-13 17:25:49 -0700413 // We store hotseat items in canonical form which is this orientation invariant position
414 // in the hotseat
415 if (context instanceof Launcher && screen < 0 &&
416 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
417 item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
418 } else {
419 item.screen = screen;
420 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400421
422 final ContentValues values = new ContentValues();
423 final ContentResolver cr = context.getContentResolver();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400424 item.onAddToDatabase(values);
425
Michael Jurka7578ec62011-08-03 14:11:54 -0700426 LauncherApplication app = (LauncherApplication) context.getApplicationContext();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700427 item.id = app.getLauncherProvider().generateNewId();
428 values.put(LauncherSettings.Favorites._ID, item.id);
Winson Chung3d503fb2011-07-13 17:25:49 -0700429 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700430
Michael Jurkac9d95c52011-08-29 14:03:34 -0700431 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700432 public void run() {
433 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
434 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400435
Winson Chungb1094bd2011-08-24 16:14:08 -0700436 if (sItemsIdMap.containsKey(item.id)) {
437 // we should not be adding new items in the db with the same id
438 throw new RuntimeException("Error: ItemInfo id (" + item.id + ") passed to " +
439 "addItemToDatabase already exists." + item.toString());
440 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700441 sItemsIdMap.put(item.id, item);
442 switch (item.itemType) {
443 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
444 sFolders.put(item.id, (FolderInfo) item);
Winson Chung3d503fb2011-07-13 17:25:49 -0700445 // Fall through
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700446 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
447 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Winson Chung3d503fb2011-07-13 17:25:49 -0700448 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
449 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohen4eac29a2011-07-11 17:53:37 -0700450 sWorkspaceItems.add(item);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700451 }
452 break;
453 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
454 sAppWidgets.add((LauncherAppWidgetInfo) item);
455 break;
456 }
457 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700458 };
459
460 if (sWorkerThread.getThreadId() == Process.myTid()) {
461 r.run();
462 } else {
463 sWorker.post(r);
464 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700465 }
466
Joe Onorato9c1289c2009-08-17 11:03:03 -0400467 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700468 * Creates a new unique child id, for a given cell span across all layouts.
469 */
Michael Jurka845ba3b2010-09-28 17:09:46 -0700470 static int getCellLayoutChildId(
Winson Chung3d503fb2011-07-13 17:25:49 -0700471 long container, int screen, int localCellX, int localCellY, int spanX, int spanY) {
472 return (((int) container & 0xFF) << 24)
Michael Jurka845ba3b2010-09-28 17:09:46 -0700473 | (screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
Winson Chungaafa03c2010-06-11 17:34:16 -0700474 }
475
Adam Cohend22015c2010-07-26 22:02:18 -0700476 static int getCellCountX() {
477 return mCellCountX;
Winson Chungaafa03c2010-06-11 17:34:16 -0700478 }
479
Adam Cohend22015c2010-07-26 22:02:18 -0700480 static int getCellCountY() {
481 return mCellCountY;
Winson Chungaafa03c2010-06-11 17:34:16 -0700482 }
483
484 /**
485 * Updates the model orientation helper to take into account the current layout dimensions
486 * when performing local/canonical coordinate transformations.
487 */
488 static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) {
Adam Cohend22015c2010-07-26 22:02:18 -0700489 mCellCountX = shortAxisCellCount;
490 mCellCountY = longAxisCellCount;
Winson Chungaafa03c2010-06-11 17:34:16 -0700491 }
492
493 /**
Michael Jurkac9d95c52011-08-29 14:03:34 -0700494 * Removes the specified item from the database
495 * @param context
496 * @param item
Joe Onorato9c1289c2009-08-17 11:03:03 -0400497 */
Michael Jurkac9d95c52011-08-29 14:03:34 -0700498 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400499 final ContentResolver cr = context.getContentResolver();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700500 final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
Michael Jurka83df1882011-08-31 20:59:26 -0700501 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700502 public void run() {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700503 cr.delete(uriToDelete, null, null);
504 switch (item.itemType) {
505 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
506 sFolders.remove(item.id);
507 sWorkspaceItems.remove(item);
508 break;
509 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
510 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
511 sWorkspaceItems.remove(item);
512 break;
513 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
514 sAppWidgets.remove((LauncherAppWidgetInfo) item);
515 break;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700516 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700517 sItemsIdMap.remove(item.id);
518 sDbIconCache.remove(item);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700519 }
Michael Jurka83df1882011-08-31 20:59:26 -0700520 };
521 if (sWorkerThread.getThreadId() == Process.myTid()) {
522 r.run();
523 } else {
524 sWorker.post(r);
525 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400526 }
527
528 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400529 * Remove the contents of the specified folder from the database
530 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700531 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400532 final ContentResolver cr = context.getContentResolver();
533
Michael Jurkac9d95c52011-08-29 14:03:34 -0700534 Runnable r = new Runnable() {
535 public void run() {
536 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
537 sItemsIdMap.remove(info.id);
538 sFolders.remove(info.id);
539 sDbIconCache.remove(info);
540 sWorkspaceItems.remove(info);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700541
Michael Jurkac9d95c52011-08-29 14:03:34 -0700542 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
543 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
544 for (ItemInfo childInfo : info.contents) {
545 sItemsIdMap.remove(childInfo.id);
546 sDbIconCache.remove(childInfo);
Adam Cohenafb01ee2011-06-23 15:38:03 -0700547 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700548 }
549 };
550 if (sWorkerThread.getThreadId() == Process.myTid()) {
551 r.run();
552 } else {
553 sWorker.post(r);
554 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400555 }
556
557 /**
558 * Set this as the current Launcher activity object for the loader.
559 */
560 public void initialize(Callbacks callbacks) {
561 synchronized (mLock) {
562 mCallbacks = new WeakReference<Callbacks>(callbacks);
563 }
564 }
565
Joe Onorato1d8e7bb2009-10-15 19:49:43 -0700566 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400567 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
568 * ACTION_PACKAGE_CHANGED.
569 */
Narayan Kamathcb1a4772011-06-28 13:46:59 +0100570 @Override
Joe Onoratof99f8c12009-10-31 17:27:36 -0400571 public void onReceive(Context context, Intent intent) {
Joe Onorato36115782010-06-17 13:28:48 -0400572 if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
Winson Chungaafa03c2010-06-11 17:34:16 -0700573
Joe Onorato36115782010-06-17 13:28:48 -0400574 final String action = intent.getAction();
Joe Onoratof99f8c12009-10-31 17:27:36 -0400575
Joe Onorato36115782010-06-17 13:28:48 -0400576 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
577 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
578 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
579 final String packageName = intent.getData().getSchemeSpecificPart();
580 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400581
Joe Onorato36115782010-06-17 13:28:48 -0400582 int op = PackageUpdatedTask.OP_NONE;
583
584 if (packageName == null || packageName.length() == 0) {
585 // they sent us a bad intent
586 return;
587 }
588
589 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
590 op = PackageUpdatedTask.OP_UPDATE;
591 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
592 if (!replacing) {
593 op = PackageUpdatedTask.OP_REMOVE;
594 }
595 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
596 // later, we will update the package at this time
597 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
598 if (!replacing) {
599 op = PackageUpdatedTask.OP_ADD;
600 } else {
601 op = PackageUpdatedTask.OP_UPDATE;
602 }
603 }
604
605 if (op != PackageUpdatedTask.OP_NONE) {
606 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
607 }
608
609 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Joe Onoratocec58332010-10-07 14:37:40 -0400610 // First, schedule to add these apps back in.
611 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
612 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
613 // Then, rebind everything.
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700614 startLoaderFromBackground();
Joe Onorato36115782010-06-17 13:28:48 -0400615 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
616 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
617 enqueuePackageUpdated(new PackageUpdatedTask(
618 PackageUpdatedTask.OP_UNAVAILABLE, packages));
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700619 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Reena Lee93f824a2011-09-23 17:20:28 -0700620 // If we have changed locale we need to clear out the labels in all apps/workspace.
621 forceReload();
622 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
623 // Check if configuration change was an mcc/mnc change which would affect app resources
624 // and we would need to clear out the labels in all apps/workspace. Same handling as
625 // above for ACTION_LOCALE_CHANGED
626 Configuration currentConfig = context.getResources().getConfiguration();
Reena Lee99a73f32011-10-24 17:27:37 -0700627 if (mPreviousConfigMcc != currentConfig.mcc) {
Reena Lee93f824a2011-09-23 17:20:28 -0700628 Log.d(TAG, "Reload apps on config change. curr_mcc:"
Reena Lee99a73f32011-10-24 17:27:37 -0700629 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
Reena Lee93f824a2011-09-23 17:20:28 -0700630 forceReload();
631 }
632 // Update previousConfig
Reena Lee99a73f32011-10-24 17:27:37 -0700633 mPreviousConfigMcc = currentConfig.mcc;
Winson Chungcbf7c4d2011-08-23 11:58:54 -0700634 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
635 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
Michael Jurkaec9788e2011-08-29 11:24:45 -0700636 if (mCallbacks != null) {
637 Callbacks callbacks = mCallbacks.get();
638 if (callbacks != null) {
639 callbacks.bindSearchablesChanged();
640 }
Winson Chungcfdf7ee2011-08-25 11:38:34 -0700641 }
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700642 }
643 }
644
Reena Lee93f824a2011-09-23 17:20:28 -0700645 private void forceReload() {
646 synchronized (mLock) {
647 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
648 // mWorkspaceLoaded to true later
649 stopLoaderLocked();
650 mAllAppsLoaded = false;
651 mWorkspaceLoaded = false;
652 }
653 // Do this here because if the launcher activity is running it will be restarted.
654 // If it's not running startLoaderFromBackground will merely tell it that it needs
655 // to reload.
656 startLoaderFromBackground();
657 }
658
Joe Onoratoe9ad59e2010-10-29 17:35:36 -0700659 /**
660 * When the launcher is in the background, it's possible for it to miss paired
661 * configuration changes. So whenever we trigger the loader from the background
662 * tell the launcher that it needs to re-run the loader when it comes back instead
663 * of doing it now.
664 */
665 public void startLoaderFromBackground() {
666 boolean runLoader = false;
667 if (mCallbacks != null) {
668 Callbacks callbacks = mCallbacks.get();
669 if (callbacks != null) {
670 // Only actually run the loader if they're not paused.
671 if (!callbacks.setLoadOnResume()) {
672 runLoader = true;
673 }
674 }
675 }
676 if (runLoader) {
677 startLoader(mApp, false);
Joe Onorato790c2d92010-06-11 00:14:11 -0700678 }
Joe Onorato36115782010-06-17 13:28:48 -0400679 }
Joe Onoratof99f8c12009-10-31 17:27:36 -0400680
Reena Lee93f824a2011-09-23 17:20:28 -0700681 // If there is already a loader task running, tell it to stop.
682 // returns true if isLaunching() was true on the old task
683 private boolean stopLoaderLocked() {
684 boolean isLaunching = false;
685 LoaderTask oldTask = mLoaderTask;
686 if (oldTask != null) {
687 if (oldTask.isLaunching()) {
688 isLaunching = true;
689 }
690 oldTask.stopLocked();
691 }
692 return isLaunching;
693 }
694
Joe Onorato36115782010-06-17 13:28:48 -0400695 public void startLoader(Context context, boolean isLaunching) {
696 synchronized (mLock) {
697 if (DEBUG_LOADERS) {
698 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
699 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400700
Joe Onorato36115782010-06-17 13:28:48 -0400701 // Don't bother to start the thread if we know it's not going to do anything
702 if (mCallbacks != null && mCallbacks.get() != null) {
703 // If there is already one running, tell it to stop.
Reena Lee93f824a2011-09-23 17:20:28 -0700704 // also, don't downgrade isLaunching if we're already running
705 isLaunching = isLaunching || stopLoaderLocked();
Joe Onorato36115782010-06-17 13:28:48 -0400706 mLoaderTask = new LoaderTask(context, isLaunching);
Winson Chung7ed37742011-09-08 15:45:51 -0700707 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700708 sWorker.post(mLoaderTask);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400709 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400710 }
711 }
712
Joe Onorato36115782010-06-17 13:28:48 -0400713 public void stopLoader() {
714 synchronized (mLock) {
715 if (mLoaderTask != null) {
716 mLoaderTask.stopLocked();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400717 }
718 }
Joe Onorato36115782010-06-17 13:28:48 -0400719 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400720
Michael Jurkac57b7a82011-08-09 22:02:20 -0700721 public boolean isAllAppsLoaded() {
722 return mAllAppsLoaded;
723 }
724
Joe Onorato36115782010-06-17 13:28:48 -0400725 /**
726 * Runnable for the thread that loads the contents of the launcher:
727 * - workspace icons
728 * - widgets
729 * - all apps icons
730 */
731 private class LoaderTask implements Runnable {
732 private Context mContext;
733 private Thread mWaitThread;
734 private boolean mIsLaunching;
735 private boolean mStopped;
736 private boolean mLoadAndBindStepFinished;
Winson Chungc3eecff2011-07-11 17:44:15 -0700737 private HashMap<Object, CharSequence> mLabelCache;
Joe Onorato36115782010-06-17 13:28:48 -0400738
739 LoaderTask(Context context, boolean isLaunching) {
740 mContext = context;
741 mIsLaunching = isLaunching;
Winson Chungc3eecff2011-07-11 17:44:15 -0700742 mLabelCache = new HashMap<Object, CharSequence>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400743 }
744
Joe Onorato36115782010-06-17 13:28:48 -0400745 boolean isLaunching() {
746 return mIsLaunching;
747 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400748
Joe Onorato36115782010-06-17 13:28:48 -0400749 private void loadAndBindWorkspace() {
750 // Load the workspace
Joe Onorato36115782010-06-17 13:28:48 -0400751 if (DEBUG_LOADERS) {
752 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400753 }
Michael Jurka288a36b2011-07-12 16:53:48 -0700754
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700755 if (!mWorkspaceLoaded) {
Joe Onorato36115782010-06-17 13:28:48 -0400756 loadWorkspace();
Reena Lee93f824a2011-09-23 17:20:28 -0700757 synchronized (LoaderTask.this) {
758 if (mStopped) {
759 return;
760 }
761 mWorkspaceLoaded = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400762 }
763 }
764
Joe Onorato36115782010-06-17 13:28:48 -0400765 // Bind the workspace
766 bindWorkspace();
767 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400768
Joe Onorato36115782010-06-17 13:28:48 -0400769 private void waitForIdle() {
770 // Wait until the either we're stopped or the other threads are done.
771 // This way we don't start loading all apps until the workspace has settled
772 // down.
773 synchronized (LoaderTask.this) {
774 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onoratocc67f472010-06-08 10:54:30 -0700775
Joe Onorato36115782010-06-17 13:28:48 -0400776 mHandler.postIdle(new Runnable() {
777 public void run() {
778 synchronized (LoaderTask.this) {
779 mLoadAndBindStepFinished = true;
780 if (DEBUG_LOADERS) {
781 Log.d(TAG, "done with previous binding step");
Daniel Sandler843e8602010-06-07 14:59:01 -0400782 }
Joe Onorato36115782010-06-17 13:28:48 -0400783 LoaderTask.this.notify();
Daniel Sandler843e8602010-06-07 14:59:01 -0400784 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400785 }
Joe Onorato36115782010-06-17 13:28:48 -0400786 });
787
788 while (!mStopped && !mLoadAndBindStepFinished) {
789 try {
790 this.wait();
791 } catch (InterruptedException ex) {
792 // Ignore
Daniel Sandler843e8602010-06-07 14:59:01 -0400793 }
794 }
Joe Onorato36115782010-06-17 13:28:48 -0400795 if (DEBUG_LOADERS) {
796 Log.d(TAG, "waited "
Winson Chungaafa03c2010-06-11 17:34:16 -0700797 + (SystemClock.uptimeMillis()-workspaceWaitTime)
Joe Onorato36115782010-06-17 13:28:48 -0400798 + "ms for previous step to finish binding");
799 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400800 }
Joe Onorato36115782010-06-17 13:28:48 -0400801 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400802
Joe Onorato36115782010-06-17 13:28:48 -0400803 public void run() {
804 // Optimize for end-user experience: if the Launcher is up and // running with the
805 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
806 // workspace first (default).
807 final Callbacks cbk = mCallbacks.get();
808 final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
Daniel Sandler843e8602010-06-07 14:59:01 -0400809
Joe Onorato36115782010-06-17 13:28:48 -0400810 keep_running: {
Daniel Sandler843e8602010-06-07 14:59:01 -0400811 // Elevate priority when Home launches for the first time to avoid
812 // starving at boot time. Staring at a blank home is not cool.
813 synchronized (mLock) {
Winson Chungaac01e12011-08-17 10:37:13 -0700814 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
815 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
Daniel Sandler843e8602010-06-07 14:59:01 -0400816 android.os.Process.setThreadPriority(mIsLaunching
817 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
818 }
Daniel Sandler843e8602010-06-07 14:59:01 -0400819 if (loadWorkspaceFirst) {
820 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
821 loadAndBindWorkspace();
822 } else {
823 if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700824 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400825 }
826
Joe Onorato36115782010-06-17 13:28:48 -0400827 if (mStopped) {
828 break keep_running;
829 }
830
831 // Whew! Hard work done. Slow us down, and wait until the UI thread has
832 // settled down.
Daniel Sandler843e8602010-06-07 14:59:01 -0400833 synchronized (mLock) {
834 if (mIsLaunching) {
Winson Chungaac01e12011-08-17 10:37:13 -0700835 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
Daniel Sandler843e8602010-06-07 14:59:01 -0400836 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
837 }
838 }
Joe Onorato36115782010-06-17 13:28:48 -0400839 waitForIdle();
Daniel Sandler843e8602010-06-07 14:59:01 -0400840
841 // second step
842 if (loadWorkspaceFirst) {
843 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
Joe Onoratocc67f472010-06-08 10:54:30 -0700844 loadAndBindAllApps();
Daniel Sandler843e8602010-06-07 14:59:01 -0400845 } else {
846 if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
847 loadAndBindWorkspace();
848 }
Winson Chung7ed37742011-09-08 15:45:51 -0700849
850 // Restore the default thread priority after we are done loading items
851 synchronized (mLock) {
852 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
853 }
Joe Onorato36115782010-06-17 13:28:48 -0400854 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400855
Winson Chungaac01e12011-08-17 10:37:13 -0700856
857 // Update the saved icons if necessary
858 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
Winson Chungb1094bd2011-08-24 16:14:08 -0700859 for (Object key : sDbIconCache.keySet()) {
860 updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
Winson Chungaac01e12011-08-17 10:37:13 -0700861 }
Winson Chungb1094bd2011-08-24 16:14:08 -0700862 sDbIconCache.clear();
Winson Chungaac01e12011-08-17 10:37:13 -0700863
Joe Onorato36115782010-06-17 13:28:48 -0400864 // Clear out this reference, otherwise we end up holding it until all of the
865 // callback runnables are done.
866 mContext = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400867
Joe Onorato36115782010-06-17 13:28:48 -0400868 synchronized (mLock) {
869 // If we are still the last one to be scheduled, remove ourselves.
870 if (mLoaderTask == this) {
871 mLoaderTask = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400872 }
Joe Onorato36115782010-06-17 13:28:48 -0400873 }
Joe Onorato36115782010-06-17 13:28:48 -0400874 }
875
876 public void stopLocked() {
877 synchronized (LoaderTask.this) {
878 mStopped = true;
879 this.notify();
880 }
881 }
882
883 /**
884 * Gets the callbacks object. If we've been stopped, or if the launcher object
885 * has somehow been garbage collected, return null instead. Pass in the Callbacks
886 * object that was around when the deferred message was scheduled, and if there's
887 * a new Callbacks object around then also return null. This will save us from
888 * calling onto it with data that will be ignored.
889 */
890 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
891 synchronized (mLock) {
892 if (mStopped) {
893 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -0400894 }
Joe Onorato36115782010-06-17 13:28:48 -0400895
896 if (mCallbacks == null) {
897 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -0400898 }
Joe Onorato36115782010-06-17 13:28:48 -0400899
900 final Callbacks callbacks = mCallbacks.get();
901 if (callbacks != oldCallbacks) {
902 return null;
903 }
904 if (callbacks == null) {
905 Log.w(TAG, "no mCallbacks");
906 return null;
907 }
908
909 return callbacks;
910 }
911 }
912
913 // check & update map of what's occupied; used to discard overlapping/invalid items
914 private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
Winson Chungf30ad5f2011-08-08 10:55:42 -0700915 int containerIndex = item.screen;
916 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Winson Chungf30ad5f2011-08-08 10:55:42 -0700917 // Return early if we detect that an item is under the hotseat button
918 if (Hotseat.isAllAppsButtonRank(item.screen)) {
919 return false;
920 }
Winson Chung6ba2a1b2011-09-02 16:22:11 -0700921
922 // We use the last index to refer to the hotseat and the screen as the rank, so
923 // test and update the occupied state accordingly
924 if (occupied[Launcher.SCREEN_COUNT][item.screen][0] != null) {
925 Log.e(TAG, "Error loading shortcut into hotseat " + item
926 + " into position (" + item.screen + ":" + item.cellX + "," + item.cellY
927 + ") occupied by " + occupied[Launcher.SCREEN_COUNT][item.screen][0]);
928 return false;
929 } else {
930 occupied[Launcher.SCREEN_COUNT][item.screen][0] = item;
931 return true;
932 }
Winson Chungf30ad5f2011-08-08 10:55:42 -0700933 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
934 // Skip further checking if it is not the hotseat or workspace container
Daniel Sandler8802e962010-05-26 16:28:16 -0400935 return true;
936 }
Winson Chungf30ad5f2011-08-08 10:55:42 -0700937
Winson Chung6ba2a1b2011-09-02 16:22:11 -0700938 // Check if any workspace icons overlap with each other
Joe Onorato36115782010-06-17 13:28:48 -0400939 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
940 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Winson Chungf30ad5f2011-08-08 10:55:42 -0700941 if (occupied[containerIndex][x][y] != null) {
Joe Onorato36115782010-06-17 13:28:48 -0400942 Log.e(TAG, "Error loading shortcut " + item
Winson Chungf30ad5f2011-08-08 10:55:42 -0700943 + " into cell (" + containerIndex + "-" + item.screen + ":"
Joe Onorato36115782010-06-17 13:28:48 -0400944 + x + "," + y
Winson Chungaafa03c2010-06-11 17:34:16 -0700945 + ") occupied by "
Winson Chungf30ad5f2011-08-08 10:55:42 -0700946 + occupied[containerIndex][x][y]);
Joe Onorato36115782010-06-17 13:28:48 -0400947 return false;
948 }
949 }
950 }
951 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
952 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Winson Chungf30ad5f2011-08-08 10:55:42 -0700953 occupied[containerIndex][x][y] = item;
Joe Onorato36115782010-06-17 13:28:48 -0400954 }
955 }
Winson Chungf30ad5f2011-08-08 10:55:42 -0700956
Joe Onorato36115782010-06-17 13:28:48 -0400957 return true;
958 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400959
Joe Onorato36115782010-06-17 13:28:48 -0400960 private void loadWorkspace() {
961 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400962
Joe Onorato36115782010-06-17 13:28:48 -0400963 final Context context = mContext;
964 final ContentResolver contentResolver = context.getContentResolver();
965 final PackageManager manager = context.getPackageManager();
966 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
967 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato3c2f7e12009-10-31 19:17:31 -0400968
Adam Cohen4eac29a2011-07-11 17:53:37 -0700969 sWorkspaceItems.clear();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700970 sAppWidgets.clear();
971 sFolders.clear();
972 sItemsIdMap.clear();
Winson Chungb1094bd2011-08-24 16:14:08 -0700973 sDbIconCache.clear();
Romain Guy5c16f3e2010-01-12 17:24:58 -0800974
Joe Onorato36115782010-06-17 13:28:48 -0400975 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400976
Joe Onorato36115782010-06-17 13:28:48 -0400977 final Cursor c = contentResolver.query(
Winson Chunge61e93e2011-09-12 10:59:49 -0700978 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
Daniel Sandler8802e962010-05-26 16:28:16 -0400979
Winson Chungf30ad5f2011-08-08 10:55:42 -0700980 // +1 for the hotseat (it can be larger than the workspace)
Winson Chung36f97362011-09-07 11:20:10 -0700981 // Load workspace in reverse order to ensure that latest items are loaded first (and
982 // before any earlier duplicates)
Adam Cohend22015c2010-07-26 22:02:18 -0700983 final ItemInfo occupied[][][] =
Winson Chungf30ad5f2011-08-08 10:55:42 -0700984 new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];
Joe Onorato9c1289c2009-08-17 11:03:03 -0400985
Joe Onorato36115782010-06-17 13:28:48 -0400986 try {
987 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
988 final int intentIndex = c.getColumnIndexOrThrow
989 (LauncherSettings.Favorites.INTENT);
990 final int titleIndex = c.getColumnIndexOrThrow
991 (LauncherSettings.Favorites.TITLE);
992 final int iconTypeIndex = c.getColumnIndexOrThrow(
993 LauncherSettings.Favorites.ICON_TYPE);
994 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
995 final int iconPackageIndex = c.getColumnIndexOrThrow(
996 LauncherSettings.Favorites.ICON_PACKAGE);
997 final int iconResourceIndex = c.getColumnIndexOrThrow(
998 LauncherSettings.Favorites.ICON_RESOURCE);
999 final int containerIndex = c.getColumnIndexOrThrow(
1000 LauncherSettings.Favorites.CONTAINER);
1001 final int itemTypeIndex = c.getColumnIndexOrThrow(
1002 LauncherSettings.Favorites.ITEM_TYPE);
1003 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1004 LauncherSettings.Favorites.APPWIDGET_ID);
1005 final int screenIndex = c.getColumnIndexOrThrow(
1006 LauncherSettings.Favorites.SCREEN);
1007 final int cellXIndex = c.getColumnIndexOrThrow
1008 (LauncherSettings.Favorites.CELLX);
1009 final int cellYIndex = c.getColumnIndexOrThrow
1010 (LauncherSettings.Favorites.CELLY);
1011 final int spanXIndex = c.getColumnIndexOrThrow
1012 (LauncherSettings.Favorites.SPANX);
1013 final int spanYIndex = c.getColumnIndexOrThrow(
1014 LauncherSettings.Favorites.SPANY);
1015 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1016 final int displayModeIndex = c.getColumnIndexOrThrow(
1017 LauncherSettings.Favorites.DISPLAY_MODE);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001018
Joe Onorato36115782010-06-17 13:28:48 -04001019 ShortcutInfo info;
1020 String intentDescription;
1021 LauncherAppWidgetInfo appWidgetInfo;
1022 int container;
1023 long id;
1024 Intent intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001025
Joe Onorato36115782010-06-17 13:28:48 -04001026 while (!mStopped && c.moveToNext()) {
1027 try {
1028 int itemType = c.getInt(itemTypeIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001029
Joe Onorato36115782010-06-17 13:28:48 -04001030 switch (itemType) {
1031 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1032 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1033 intentDescription = c.getString(intentIndex);
1034 try {
1035 intent = Intent.parseUri(intentDescription, 0);
1036 } catch (URISyntaxException e) {
1037 continue;
1038 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001039
Joe Onorato36115782010-06-17 13:28:48 -04001040 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1041 info = getShortcutInfo(manager, intent, context, c, iconIndex,
Winson Chungc3eecff2011-07-11 17:44:15 -07001042 titleIndex, mLabelCache);
Joe Onorato36115782010-06-17 13:28:48 -04001043 } else {
1044 info = getShortcutInfo(c, context, iconTypeIndex,
1045 iconPackageIndex, iconResourceIndex, iconIndex,
1046 titleIndex);
1047 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001048
Joe Onorato36115782010-06-17 13:28:48 -04001049 if (info != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001050 info.intent = intent;
1051 info.id = c.getLong(idIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001052 container = c.getInt(containerIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001053 info.container = container;
1054 info.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -07001055 info.cellX = c.getInt(cellXIndex);
1056 info.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001057
Daniel Sandler8802e962010-05-26 16:28:16 -04001058 // check & update map of what's occupied
Joe Onorato36115782010-06-17 13:28:48 -04001059 if (!checkItemPlacement(occupied, info)) {
Daniel Sandler8802e962010-05-26 16:28:16 -04001060 break;
1061 }
1062
Joe Onorato9c1289c2009-08-17 11:03:03 -04001063 switch (container) {
Joe Onorato36115782010-06-17 13:28:48 -04001064 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
Winson Chung3d503fb2011-07-13 17:25:49 -07001065 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
Adam Cohen4eac29a2011-07-11 17:53:37 -07001066 sWorkspaceItems.add(info);
Joe Onorato36115782010-06-17 13:28:48 -04001067 break;
1068 default:
1069 // Item is in a user folder
Adam Cohendf2cc412011-04-27 16:56:57 -07001070 FolderInfo folderInfo =
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001071 findOrMakeFolder(sFolders, container);
Joe Onorato36115782010-06-17 13:28:48 -04001072 folderInfo.add(info);
1073 break;
1074 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001075 sItemsIdMap.put(info.id, info);
Joe Onorato17a89222011-02-08 17:26:11 -08001076
1077 // now that we've loaded everthing re-save it with the
1078 // icon in case it disappears somehow.
Winson Chungb1094bd2011-08-24 16:14:08 -07001079 queueIconToBeChecked(sDbIconCache, info, c, iconIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001080 } else {
1081 // Failed to load the shortcut, probably because the
1082 // activity manager couldn't resolve it (maybe the app
1083 // was uninstalled), or the db row was somehow screwed up.
1084 // Delete it.
1085 id = c.getLong(idIndex);
1086 Log.e(TAG, "Error loading shortcut " + id + ", removing it");
1087 contentResolver.delete(LauncherSettings.Favorites.getContentUri(
1088 id, false), null, null);
1089 }
1090 break;
1091
Adam Cohendf2cc412011-04-27 16:56:57 -07001092 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Joe Onorato36115782010-06-17 13:28:48 -04001093 id = c.getLong(idIndex);
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001094 FolderInfo folderInfo = findOrMakeFolder(sFolders, id);
Joe Onorato36115782010-06-17 13:28:48 -04001095
Winson Chungaafa03c2010-06-11 17:34:16 -07001096 folderInfo.title = c.getString(titleIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001097 folderInfo.id = id;
1098 container = c.getInt(containerIndex);
1099 folderInfo.container = container;
1100 folderInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -07001101 folderInfo.cellX = c.getInt(cellXIndex);
1102 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001103
1104 // check & update map of what's occupied
1105 if (!checkItemPlacement(occupied, folderInfo)) {
1106 break;
1107 }
Joe Onorato36115782010-06-17 13:28:48 -04001108 switch (container) {
1109 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
Winson Chung3d503fb2011-07-13 17:25:49 -07001110 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
Adam Cohen4eac29a2011-07-11 17:53:37 -07001111 sWorkspaceItems.add(folderInfo);
Joe Onorato36115782010-06-17 13:28:48 -04001112 break;
1113 }
1114
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001115 sItemsIdMap.put(folderInfo.id, folderInfo);
1116 sFolders.put(folderInfo.id, folderInfo);
Joe Onorato36115782010-06-17 13:28:48 -04001117 break;
1118
Joe Onorato36115782010-06-17 13:28:48 -04001119 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1120 // Read all Launcher-specific widget details
1121 int appWidgetId = c.getInt(appWidgetIdIndex);
1122 id = c.getLong(idIndex);
1123
1124 final AppWidgetProviderInfo provider =
1125 widgets.getAppWidgetInfo(appWidgetId);
Winson Chungaafa03c2010-06-11 17:34:16 -07001126
Joe Onorato36115782010-06-17 13:28:48 -04001127 if (!isSafeMode && (provider == null || provider.provider == null ||
1128 provider.provider.getPackageName() == null)) {
Adam Cohen16d7ffc2011-10-05 17:49:14 -07001129 String log = "Deleting widget that isn't installed anymore: id="
1130 + id + " appWidgetId=" + appWidgetId;
1131 Log.e(TAG, log);
1132 Launcher.sDumpLogs.add(log);
Joe Onorato36115782010-06-17 13:28:48 -04001133 itemsToRemove.add(id);
1134 } else {
Michael Jurkac9d95c52011-08-29 14:03:34 -07001135 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
Joe Onorato36115782010-06-17 13:28:48 -04001136 appWidgetInfo.id = id;
1137 appWidgetInfo.screen = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -07001138 appWidgetInfo.cellX = c.getInt(cellXIndex);
1139 appWidgetInfo.cellY = c.getInt(cellYIndex);
1140 appWidgetInfo.spanX = c.getInt(spanXIndex);
1141 appWidgetInfo.spanY = c.getInt(spanYIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001142
1143 container = c.getInt(containerIndex);
Winson Chung3d503fb2011-07-13 17:25:49 -07001144 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1145 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Joe Onorato36115782010-06-17 13:28:48 -04001146 Log.e(TAG, "Widget found where container "
Winson Chung3d503fb2011-07-13 17:25:49 -07001147 + "!= CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
Joe Onorato36115782010-06-17 13:28:48 -04001148 continue;
1149 }
1150 appWidgetInfo.container = c.getInt(containerIndex);
1151
1152 // check & update map of what's occupied
1153 if (!checkItemPlacement(occupied, appWidgetInfo)) {
1154 break;
1155 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001156 sItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
1157 sAppWidgets.add(appWidgetInfo);
Joe Onorato36115782010-06-17 13:28:48 -04001158 }
1159 break;
Romain Guy5c16f3e2010-01-12 17:24:58 -08001160 }
Joe Onorato36115782010-06-17 13:28:48 -04001161 } catch (Exception e) {
1162 Log.w(TAG, "Desktop items loading interrupted:", e);
Romain Guy5c16f3e2010-01-12 17:24:58 -08001163 }
1164 }
Joe Onorato36115782010-06-17 13:28:48 -04001165 } finally {
1166 c.close();
1167 }
Romain Guy5c16f3e2010-01-12 17:24:58 -08001168
Joe Onorato36115782010-06-17 13:28:48 -04001169 if (itemsToRemove.size() > 0) {
1170 ContentProviderClient client = contentResolver.acquireContentProviderClient(
1171 LauncherSettings.Favorites.CONTENT_URI);
1172 // Remove dead items
1173 for (long id : itemsToRemove) {
1174 if (DEBUG_LOADERS) {
1175 Log.d(TAG, "Removed id = " + id);
1176 }
1177 // Don't notify content observers
1178 try {
1179 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
1180 null, null);
1181 } catch (RemoteException e) {
1182 Log.w(TAG, "Could not remove id = " + id);
Daniel Sandler8802e962010-05-26 16:28:16 -04001183 }
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001184 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001185 }
1186
Joe Onorato36115782010-06-17 13:28:48 -04001187 if (DEBUG_LOADERS) {
1188 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
1189 Log.d(TAG, "workspace layout: ");
Adam Cohend22015c2010-07-26 22:02:18 -07001190 for (int y = 0; y < mCellCountY; y++) {
Joe Onorato36115782010-06-17 13:28:48 -04001191 String line = "";
1192 for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
1193 if (s > 0) {
1194 line += " | ";
1195 }
Adam Cohend22015c2010-07-26 22:02:18 -07001196 for (int x = 0; x < mCellCountX; x++) {
Joe Onorato36115782010-06-17 13:28:48 -04001197 line += ((occupied[s][x][y] != null) ? "#" : ".");
1198 }
1199 }
1200 Log.d(TAG, "[ " + line + " ]");
Joe Onorato9c1289c2009-08-17 11:03:03 -04001201 }
Joe Onorato36115782010-06-17 13:28:48 -04001202 }
1203 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001204
Joe Onorato36115782010-06-17 13:28:48 -04001205 /**
1206 * Read everything out of our database.
1207 */
1208 private void bindWorkspace() {
1209 final long t = SystemClock.uptimeMillis();
1210
1211 // Don't use these two variables in any of the callback runnables.
1212 // Otherwise we hold a reference to them.
1213 final Callbacks oldCallbacks = mCallbacks.get();
1214 if (oldCallbacks == null) {
1215 // This launcher has exited and nobody bothered to tell us. Just bail.
1216 Log.w(TAG, "LoaderTask running with no launcher");
1217 return;
1218 }
1219
1220 int N;
1221 // Tell the workspace that we're about to start firing items at it
1222 mHandler.post(new Runnable() {
1223 public void run() {
1224 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1225 if (callbacks != null) {
1226 callbacks.startBinding();
1227 }
1228 }
1229 });
Winson Chung603bcb92011-09-02 11:45:39 -07001230
1231 // Unbind previously bound workspace items to prevent a leak of AppWidgetHostViews.
1232 final ArrayList<ItemInfo> workspaceItems = unbindWorkspaceItemsOnMainThread();
1233
Joe Onorato36115782010-06-17 13:28:48 -04001234 // Add the items to the workspace.
Winson Chung603bcb92011-09-02 11:45:39 -07001235 N = workspaceItems.size();
Joe Onorato36115782010-06-17 13:28:48 -04001236 for (int i=0; i<N; i+=ITEMS_CHUNK) {
1237 final int start = i;
1238 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001239 mHandler.post(new Runnable() {
1240 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001241 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001242 if (callbacks != null) {
Winson Chung603bcb92011-09-02 11:45:39 -07001243 callbacks.bindItems(workspaceItems, start, start+chunkSize);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001244 }
1245 }
1246 });
Joe Onorato36115782010-06-17 13:28:48 -04001247 }
Winson Chung603bcb92011-09-02 11:45:39 -07001248 // Ensure that we don't use the same folders data structure on the main thread
1249 final HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>(sFolders);
Joe Onorato36115782010-06-17 13:28:48 -04001250 mHandler.post(new Runnable() {
1251 public void run() {
1252 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1253 if (callbacks != null) {
Winson Chung603bcb92011-09-02 11:45:39 -07001254 callbacks.bindFolders(folders);
Joe Onorato36115782010-06-17 13:28:48 -04001255 }
1256 }
1257 });
1258 // Wait until the queue goes empty.
1259 mHandler.post(new Runnable() {
1260 public void run() {
1261 if (DEBUG_LOADERS) {
1262 Log.d(TAG, "Going to start binding widgets soon.");
1263 }
1264 }
1265 });
1266 // Bind the widgets, one at a time.
1267 // WARNING: this is calling into the workspace from the background thread,
1268 // but since getCurrentScreen() just returns the int, we should be okay. This
1269 // is just a hint for the order, and if it's wrong, we'll be okay.
1270 // TODO: instead, we should have that push the current screen into here.
1271 final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001272 N = sAppWidgets.size();
Joe Onorato36115782010-06-17 13:28:48 -04001273 // once for the current screen
1274 for (int i=0; i<N; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001275 final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
Joe Onorato36115782010-06-17 13:28:48 -04001276 if (widget.screen == currentScreen) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001277 mHandler.post(new Runnable() {
1278 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08001279 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001280 if (callbacks != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001281 callbacks.bindAppWidget(widget);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001282 }
1283 }
1284 });
1285 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001286 }
Joe Onorato36115782010-06-17 13:28:48 -04001287 // once for the other screens
1288 for (int i=0; i<N; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001289 final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
Joe Onorato36115782010-06-17 13:28:48 -04001290 if (widget.screen != currentScreen) {
1291 mHandler.post(new Runnable() {
1292 public void run() {
1293 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1294 if (callbacks != null) {
1295 callbacks.bindAppWidget(widget);
1296 }
1297 }
1298 });
Joe Onoratocc67f472010-06-08 10:54:30 -07001299 }
1300 }
Joe Onorato36115782010-06-17 13:28:48 -04001301 // Tell the workspace that we're done.
1302 mHandler.post(new Runnable() {
1303 public void run() {
1304 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1305 if (callbacks != null) {
1306 callbacks.finishBindingItems();
1307 }
1308 }
1309 });
1310 // If we're profiling, this is the last thing in the queue.
1311 mHandler.post(new Runnable() {
1312 public void run() {
1313 if (DEBUG_LOADERS) {
1314 Log.d(TAG, "bound workspace in "
1315 + (SystemClock.uptimeMillis()-t) + "ms");
1316 }
1317 }
1318 });
1319 }
Joe Onoratocc67f472010-06-08 10:54:30 -07001320
Joe Onorato36115782010-06-17 13:28:48 -04001321 private void loadAndBindAllApps() {
1322 if (DEBUG_LOADERS) {
1323 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
1324 }
1325 if (!mAllAppsLoaded) {
1326 loadAllAppsByBatch();
Reena Lee93f824a2011-09-23 17:20:28 -07001327 synchronized (LoaderTask.this) {
1328 if (mStopped) {
1329 return;
1330 }
1331 mAllAppsLoaded = true;
Joe Onoratocc67f472010-06-08 10:54:30 -07001332 }
Joe Onorato36115782010-06-17 13:28:48 -04001333 } else {
1334 onlyBindAllApps();
1335 }
1336 }
Joe Onoratocc67f472010-06-08 10:54:30 -07001337
Joe Onorato36115782010-06-17 13:28:48 -04001338 private void onlyBindAllApps() {
1339 final Callbacks oldCallbacks = mCallbacks.get();
1340 if (oldCallbacks == null) {
1341 // This launcher has exited and nobody bothered to tell us. Just bail.
1342 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
1343 return;
1344 }
1345
1346 // shallow copy
1347 final ArrayList<ApplicationInfo> list
1348 = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
1349 mHandler.post(new Runnable() {
1350 public void run() {
1351 final long t = SystemClock.uptimeMillis();
1352 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1353 if (callbacks != null) {
1354 callbacks.bindAllApplications(list);
1355 }
1356 if (DEBUG_LOADERS) {
1357 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
1358 + (SystemClock.uptimeMillis()-t) + "ms");
1359 }
1360 }
1361 });
1362
1363 }
1364
1365 private void loadAllAppsByBatch() {
1366 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1367
1368 // Don't use these two variables in any of the callback runnables.
1369 // Otherwise we hold a reference to them.
1370 final Callbacks oldCallbacks = mCallbacks.get();
1371 if (oldCallbacks == null) {
1372 // This launcher has exited and nobody bothered to tell us. Just bail.
1373 Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)");
1374 return;
1375 }
1376
1377 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
1378 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1379
1380 final PackageManager packageManager = mContext.getPackageManager();
1381 List<ResolveInfo> apps = null;
1382
1383 int N = Integer.MAX_VALUE;
1384
1385 int startIndex;
1386 int i=0;
1387 int batchSize = -1;
1388 while (i < N && !mStopped) {
1389 if (i == 0) {
1390 mAllAppsList.clear();
1391 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1392 apps = packageManager.queryIntentActivities(mainIntent, 0);
1393 if (DEBUG_LOADERS) {
1394 Log.d(TAG, "queryIntentActivities took "
1395 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
1396 }
1397 if (apps == null) {
1398 return;
1399 }
1400 N = apps.size();
1401 if (DEBUG_LOADERS) {
1402 Log.d(TAG, "queryIntentActivities got " + N + " apps");
1403 }
1404 if (N == 0) {
1405 // There are no apps?!?
1406 return;
1407 }
1408 if (mBatchSize == 0) {
1409 batchSize = N;
1410 } else {
1411 batchSize = mBatchSize;
1412 }
1413
1414 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1415 Collections.sort(apps,
Winson Chungc3eecff2011-07-11 17:44:15 -07001416 new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
Joe Onorato36115782010-06-17 13:28:48 -04001417 if (DEBUG_LOADERS) {
1418 Log.d(TAG, "sort took "
1419 + (SystemClock.uptimeMillis()-sortTime) + "ms");
1420 }
1421 }
1422
1423 final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
1424
1425 startIndex = i;
1426 for (int j=0; i<N && j<batchSize; j++) {
1427 // This builds the icon bitmaps.
Winson Chungc3eecff2011-07-11 17:44:15 -07001428 mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),
Michael Jurkac9d95c52011-08-29 14:03:34 -07001429 mIconCache, mLabelCache));
Joe Onorato36115782010-06-17 13:28:48 -04001430 i++;
1431 }
1432
1433 final boolean first = i <= batchSize;
1434 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
1435 final ArrayList<ApplicationInfo> added = mAllAppsList.added;
1436 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1437
Joe Onoratocc67f472010-06-08 10:54:30 -07001438 mHandler.post(new Runnable() {
1439 public void run() {
1440 final long t = SystemClock.uptimeMillis();
Joe Onoratocc67f472010-06-08 10:54:30 -07001441 if (callbacks != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001442 if (first) {
1443 callbacks.bindAllApplications(added);
1444 } else {
1445 callbacks.bindAppsAdded(added);
1446 }
1447 if (DEBUG_LOADERS) {
1448 Log.d(TAG, "bound " + added.size() + " apps in "
1449 + (SystemClock.uptimeMillis() - t) + "ms");
1450 }
1451 } else {
1452 Log.i(TAG, "not binding apps: no Launcher activity");
Joe Onoratocc67f472010-06-08 10:54:30 -07001453 }
1454 }
1455 });
1456
Daniel Sandlerdca66122010-04-13 16:23:58 -04001457 if (DEBUG_LOADERS) {
Joe Onorato36115782010-06-17 13:28:48 -04001458 Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
1459 + (SystemClock.uptimeMillis()-t2) + "ms");
1460 }
1461
1462 if (mAllAppsLoadDelay > 0 && i < N) {
1463 try {
1464 if (DEBUG_LOADERS) {
1465 Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms");
1466 }
1467 Thread.sleep(mAllAppsLoadDelay);
1468 } catch (InterruptedException exc) { }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001469 }
1470 }
1471
Joe Onorato36115782010-06-17 13:28:48 -04001472 if (DEBUG_LOADERS) {
1473 Log.d(TAG, "cached all " + N + " apps in "
1474 + (SystemClock.uptimeMillis()-t) + "ms"
1475 + (mAllAppsLoadDelay > 0 ? " (including delay)" : ""));
Joe Onoratobe386092009-11-17 17:32:16 -08001476 }
1477 }
1478
1479 public void dumpState() {
Joe Onorato36115782010-06-17 13:28:48 -04001480 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
1481 Log.d(TAG, "mLoaderTask.mWaitThread=" + mWaitThread);
1482 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
1483 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
1484 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
Adam Cohen4eac29a2011-07-11 17:53:37 -07001485 Log.d(TAG, "mItems size=" + sWorkspaceItems.size());
Joe Onorato36115782010-06-17 13:28:48 -04001486 }
1487 }
1488
1489 void enqueuePackageUpdated(PackageUpdatedTask task) {
Brad Fitzpatrick700889f2010-10-11 09:40:44 -07001490 sWorker.post(task);
Joe Onorato36115782010-06-17 13:28:48 -04001491 }
1492
1493 private class PackageUpdatedTask implements Runnable {
1494 int mOp;
1495 String[] mPackages;
1496
1497 public static final int OP_NONE = 0;
1498 public static final int OP_ADD = 1;
1499 public static final int OP_UPDATE = 2;
1500 public static final int OP_REMOVE = 3; // uninstlled
1501 public static final int OP_UNAVAILABLE = 4; // external media unmounted
1502
1503
1504 public PackageUpdatedTask(int op, String[] packages) {
1505 mOp = op;
1506 mPackages = packages;
1507 }
1508
1509 public void run() {
1510 final Context context = mApp;
1511
1512 final String[] packages = mPackages;
1513 final int N = packages.length;
1514 switch (mOp) {
1515 case OP_ADD:
1516 for (int i=0; i<N; i++) {
1517 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
1518 mAllAppsList.addPackage(context, packages[i]);
1519 }
1520 break;
1521 case OP_UPDATE:
1522 for (int i=0; i<N; i++) {
1523 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
1524 mAllAppsList.updatePackage(context, packages[i]);
1525 }
1526 break;
1527 case OP_REMOVE:
1528 case OP_UNAVAILABLE:
1529 for (int i=0; i<N; i++) {
1530 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
1531 mAllAppsList.removePackage(packages[i]);
1532 }
1533 break;
1534 }
1535
1536 ArrayList<ApplicationInfo> added = null;
1537 ArrayList<ApplicationInfo> removed = null;
1538 ArrayList<ApplicationInfo> modified = null;
1539
1540 if (mAllAppsList.added.size() > 0) {
1541 added = mAllAppsList.added;
1542 mAllAppsList.added = new ArrayList<ApplicationInfo>();
1543 }
1544 if (mAllAppsList.removed.size() > 0) {
1545 removed = mAllAppsList.removed;
1546 mAllAppsList.removed = new ArrayList<ApplicationInfo>();
1547 for (ApplicationInfo info: removed) {
1548 mIconCache.remove(info.intent.getComponent());
1549 }
1550 }
1551 if (mAllAppsList.modified.size() > 0) {
1552 modified = mAllAppsList.modified;
1553 mAllAppsList.modified = new ArrayList<ApplicationInfo>();
1554 }
1555
1556 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
1557 if (callbacks == null) {
1558 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
1559 return;
1560 }
1561
1562 if (added != null) {
1563 final ArrayList<ApplicationInfo> addedFinal = added;
1564 mHandler.post(new Runnable() {
1565 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07001566 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
1567 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001568 callbacks.bindAppsAdded(addedFinal);
1569 }
1570 }
1571 });
1572 }
1573 if (modified != null) {
1574 final ArrayList<ApplicationInfo> modifiedFinal = modified;
1575 mHandler.post(new Runnable() {
1576 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07001577 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
1578 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001579 callbacks.bindAppsUpdated(modifiedFinal);
1580 }
1581 }
1582 });
1583 }
1584 if (removed != null) {
1585 final boolean permanent = mOp != OP_UNAVAILABLE;
1586 final ArrayList<ApplicationInfo> removedFinal = removed;
1587 mHandler.post(new Runnable() {
1588 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07001589 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
1590 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001591 callbacks.bindAppsRemoved(removedFinal, permanent);
1592 }
1593 }
1594 });
Joe Onoratobe386092009-11-17 17:32:16 -08001595 }
Winson Chung80baf5a2010-08-09 16:03:15 -07001596
1597 mHandler.post(new Runnable() {
1598 @Override
1599 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07001600 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
1601 if (callbacks == cb && cb != null) {
Winson Chung80baf5a2010-08-09 16:03:15 -07001602 callbacks.bindPackagesUpdated();
1603 }
1604 }
1605 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04001606 }
1607 }
1608
1609 /**
Joe Onorato56d82912010-03-07 14:32:10 -05001610 * This is called from the code that adds shortcuts from the intent receiver. This
1611 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04001612 */
Joe Onorato56d82912010-03-07 14:32:10 -05001613 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Winson Chungc3eecff2011-07-11 17:44:15 -07001614 return getShortcutInfo(manager, intent, context, null, -1, -1, null);
Joe Onorato56d82912010-03-07 14:32:10 -05001615 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001616
Joe Onorato56d82912010-03-07 14:32:10 -05001617 /**
1618 * Make an ShortcutInfo object for a shortcut that is an application.
1619 *
1620 * If c is not null, then it will be used to fill in missing data like the title and icon.
1621 */
1622 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
Winson Chungc3eecff2011-07-11 17:44:15 -07001623 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
Joe Onorato56d82912010-03-07 14:32:10 -05001624 Bitmap icon = null;
Michael Jurkac9d95c52011-08-29 14:03:34 -07001625 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001626
1627 ComponentName componentName = intent.getComponent();
1628 if (componentName == null) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001629 return null;
1630 }
1631
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001632 // TODO: See if the PackageManager knows about this case. If it doesn't
1633 // then return null & delete this.
1634
Joe Onorato56d82912010-03-07 14:32:10 -05001635 // the resource -- This may implicitly give us back the fallback icon,
1636 // but don't worry about that. All we're doing with usingFallbackIcon is
1637 // to avoid saving lots of copies of that in the database, and most apps
1638 // have icons anyway.
1639 final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
1640 if (resolveInfo != null) {
Winson Chungaac01e12011-08-17 10:37:13 -07001641 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001642 }
Joe Onorato56d82912010-03-07 14:32:10 -05001643 // the db
1644 if (icon == null) {
1645 if (c != null) {
Michael Jurka931dc972011-08-05 15:08:15 -07001646 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05001647 }
1648 }
1649 // the fallback icon
1650 if (icon == null) {
1651 icon = getFallbackIcon();
1652 info.usingFallbackIcon = true;
1653 }
1654 info.setIcon(icon);
1655
1656 // from the resource
1657 if (resolveInfo != null) {
Winson Chung5308f242011-08-18 12:12:41 -07001658 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
1659 if (labelCache != null && labelCache.containsKey(key)) {
1660 info.title = labelCache.get(key);
Winson Chungc3eecff2011-07-11 17:44:15 -07001661 } else {
1662 info.title = resolveInfo.activityInfo.loadLabel(manager);
1663 if (labelCache != null) {
Winson Chung5308f242011-08-18 12:12:41 -07001664 labelCache.put(key, info.title);
Winson Chungc3eecff2011-07-11 17:44:15 -07001665 }
1666 }
Joe Onorato56d82912010-03-07 14:32:10 -05001667 }
1668 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04001669 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05001670 if (c != null) {
1671 info.title = c.getString(titleIndex);
1672 }
1673 }
1674 // fall back to the class name of the activity
1675 if (info.title == null) {
1676 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001677 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001678 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
1679 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001680 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07001681
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001682 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08001683 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001684 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08001685 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05001686 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
1687 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001688
Joe Onorato56d82912010-03-07 14:32:10 -05001689 Bitmap icon = null;
Michael Jurkac9d95c52011-08-29 14:03:34 -07001690 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001691 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001692
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07001693 // TODO: If there's an explicit component and we can't install that, delete it.
1694
Joe Onorato56d82912010-03-07 14:32:10 -05001695 info.title = c.getString(titleIndex);
1696
Joe Onorato9c1289c2009-08-17 11:03:03 -04001697 int iconType = c.getInt(iconTypeIndex);
1698 switch (iconType) {
1699 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
1700 String packageName = c.getString(iconPackageIndex);
1701 String resourceName = c.getString(iconResourceIndex);
1702 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05001703 info.customIcon = false;
1704 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001705 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001706 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05001707 if (resources != null) {
1708 final int id = resources.getIdentifier(resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07001709 icon = Utilities.createIconBitmap(
1710 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato56d82912010-03-07 14:32:10 -05001711 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001712 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05001713 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001714 }
Joe Onorato56d82912010-03-07 14:32:10 -05001715 // the db
1716 if (icon == null) {
Michael Jurka931dc972011-08-05 15:08:15 -07001717 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05001718 }
1719 // the fallback icon
1720 if (icon == null) {
1721 icon = getFallbackIcon();
1722 info.usingFallbackIcon = true;
1723 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001724 break;
1725 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Michael Jurka931dc972011-08-05 15:08:15 -07001726 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05001727 if (icon == null) {
1728 icon = getFallbackIcon();
1729 info.customIcon = false;
1730 info.usingFallbackIcon = true;
1731 } else {
1732 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001733 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001734 break;
1735 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08001736 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05001737 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001738 info.customIcon = false;
1739 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001740 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08001741 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001742 return info;
1743 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001744
Michael Jurka931dc972011-08-05 15:08:15 -07001745 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
Joe Onorato56d82912010-03-07 14:32:10 -05001746 if (false) {
1747 Log.d(TAG, "getIconFromCursor app="
1748 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
1749 }
1750 byte[] data = c.getBlob(iconIndex);
1751 try {
Michael Jurka931dc972011-08-05 15:08:15 -07001752 return Utilities.createIconBitmap(
1753 BitmapFactory.decodeByteArray(data, 0, data.length), context);
Joe Onorato56d82912010-03-07 14:32:10 -05001754 } catch (Exception e) {
1755 return null;
1756 }
1757 }
1758
Winson Chung3d503fb2011-07-13 17:25:49 -07001759 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
1760 int cellX, int cellY, boolean notify) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07001761 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07001762 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001763
1764 return info;
1765 }
1766
Winson Chunga9abd0e2010-10-27 17:18:37 -07001767 /**
Winson Chung55cef262010-10-28 14:14:18 -07001768 * Attempts to find an AppWidgetProviderInfo that matches the given component.
1769 */
1770 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
1771 ComponentName component) {
1772 List<AppWidgetProviderInfo> widgets =
1773 AppWidgetManager.getInstance(context).getInstalledProviders();
1774 for (AppWidgetProviderInfo info : widgets) {
1775 if (info.provider.equals(component)) {
1776 return info;
1777 }
1778 }
1779 return null;
Winson Chunga9abd0e2010-10-27 17:18:37 -07001780 }
1781
Winson Chung68846fd2010-10-29 11:00:27 -07001782 /**
1783 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
1784 */
1785 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
1786 final PackageManager packageManager = context.getPackageManager();
1787 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
1788 new ArrayList<WidgetMimeTypeHandlerData>();
1789
1790 final Intent supportsIntent =
1791 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
1792 supportsIntent.setType(mimeType);
1793
1794 // Create a set of widget configuration components that we can test against
1795 final List<AppWidgetProviderInfo> widgets =
1796 AppWidgetManager.getInstance(context).getInstalledProviders();
1797 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
1798 new HashMap<ComponentName, AppWidgetProviderInfo>();
1799 for (AppWidgetProviderInfo info : widgets) {
1800 configurationComponentToWidget.put(info.configure, info);
1801 }
1802
1803 // Run through each of the intents that can handle this type of clip data, and cross
1804 // reference them with the components that are actual configuration components
1805 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
1806 PackageManager.MATCH_DEFAULT_ONLY);
1807 for (ResolveInfo info : activities) {
1808 final ActivityInfo activityInfo = info.activityInfo;
1809 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
1810 activityInfo.name);
1811 if (configurationComponentToWidget.containsKey(infoComponent)) {
1812 supportedConfigurationActivities.add(
1813 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
1814 configurationComponentToWidget.get(infoComponent)));
1815 }
1816 }
1817 return supportedConfigurationActivities;
1818 }
1819
Winson Chunga9abd0e2010-10-27 17:18:37 -07001820 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08001821 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
1822 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
1823 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
1824
1825 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001826 boolean customIcon = false;
1827 ShortcutIconResource iconResource = null;
1828
1829 if (bitmap != null && bitmap instanceof Bitmap) {
1830 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001831 customIcon = true;
1832 } else {
1833 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
1834 if (extra != null && extra instanceof ShortcutIconResource) {
1835 try {
1836 iconResource = (ShortcutIconResource) extra;
1837 final PackageManager packageManager = context.getPackageManager();
1838 Resources resources = packageManager.getResourcesForApplication(
1839 iconResource.packageName);
1840 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07001841 icon = Utilities.createIconBitmap(
1842 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08001843 } catch (Exception e) {
1844 Log.w(TAG, "Could not load shortcut icon: " + extra);
1845 }
1846 }
1847 }
1848
Michael Jurkac9d95c52011-08-29 14:03:34 -07001849 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05001850
1851 if (icon == null) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07001852 if (fallbackIcon != null) {
1853 icon = fallbackIcon;
1854 } else {
1855 icon = getFallbackIcon();
1856 info.usingFallbackIcon = true;
1857 }
Joe Onorato56d82912010-03-07 14:32:10 -05001858 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001859 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05001860
Joe Onorato0589f0f2010-02-08 13:44:00 -08001861 info.title = name;
1862 info.intent = intent;
1863 info.customIcon = customIcon;
1864 info.iconResource = iconResource;
1865
1866 return info;
1867 }
1868
Winson Chungaac01e12011-08-17 10:37:13 -07001869 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
1870 int iconIndex) {
Joe Onorato17a89222011-02-08 17:26:11 -08001871 // If apps can't be on SD, don't even bother.
1872 if (!mAppsCanBeOnExternalStorage) {
Winson Chungaac01e12011-08-17 10:37:13 -07001873 return false;
Joe Onorato17a89222011-02-08 17:26:11 -08001874 }
Joe Onorato56d82912010-03-07 14:32:10 -05001875 // If this icon doesn't have a custom icon, check to see
1876 // what's stored in the DB, and if it doesn't match what
1877 // we're going to show, store what we are going to show back
1878 // into the DB. We do this so when we're loading, if the
1879 // package manager can't find an icon (for example because
1880 // the app is on SD) then we can use that instead.
Joe Onoratoddc9c1f2010-08-30 18:30:15 -07001881 if (!info.customIcon && !info.usingFallbackIcon) {
Winson Chungaac01e12011-08-17 10:37:13 -07001882 cache.put(info, c.getBlob(iconIndex));
1883 return true;
1884 }
1885 return false;
1886 }
1887 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
1888 boolean needSave = false;
1889 try {
1890 if (data != null) {
1891 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
1892 Bitmap loaded = info.getIcon(mIconCache);
1893 needSave = !saved.sameAs(loaded);
1894 } else {
Joe Onorato56d82912010-03-07 14:32:10 -05001895 needSave = true;
1896 }
Winson Chungaac01e12011-08-17 10:37:13 -07001897 } catch (Exception e) {
1898 needSave = true;
1899 }
1900 if (needSave) {
1901 Log.d(TAG, "going to save icon bitmap for info=" + info);
1902 // This is slower than is ideal, but this only happens once
1903 // or when the app is updated with a new icon.
1904 updateItemInDatabase(context, info);
Joe Onorato56d82912010-03-07 14:32:10 -05001905 }
1906 }
1907
Joe Onorato9c1289c2009-08-17 11:03:03 -04001908 /**
Adam Cohendf2cc412011-04-27 16:56:57 -07001909 * Return an existing FolderInfo object if we have encountered this ID previously,
Joe Onorato9c1289c2009-08-17 11:03:03 -04001910 * or make a new one.
1911 */
Adam Cohendf2cc412011-04-27 16:56:57 -07001912 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001913 // See if a placeholder was created for us already
1914 FolderInfo folderInfo = folders.get(id);
Adam Cohendf2cc412011-04-27 16:56:57 -07001915 if (folderInfo == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001916 // No placeholder -- create a new instance
Michael Jurkac9d95c52011-08-29 14:03:34 -07001917 folderInfo = new FolderInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001918 folders.put(id, folderInfo);
1919 }
Adam Cohendf2cc412011-04-27 16:56:57 -07001920 return folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001921 }
1922
Joe Onorato9c1289c2009-08-17 11:03:03 -04001923 private static final Collator sCollator = Collator.getInstance();
Joe Onoratob0c27f22009-12-01 16:19:38 -08001924 public static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR
Joe Onorato9c1289c2009-08-17 11:03:03 -04001925 = new Comparator<ApplicationInfo>() {
1926 public final int compare(ApplicationInfo a, ApplicationInfo b) {
Michael Jurka5b1808d2011-07-11 19:59:46 -07001927 int result = sCollator.compare(a.title.toString(), b.title.toString());
1928 if (result == 0) {
1929 result = a.componentName.compareTo(b.componentName);
1930 }
1931 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001932 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001933 };
Winson Chung78403fe2011-01-21 15:38:02 -08001934 public static final Comparator<ApplicationInfo> APP_INSTALL_TIME_COMPARATOR
1935 = new Comparator<ApplicationInfo>() {
1936 public final int compare(ApplicationInfo a, ApplicationInfo b) {
1937 if (a.firstInstallTime < b.firstInstallTime) return 1;
1938 if (a.firstInstallTime > b.firstInstallTime) return -1;
1939 return 0;
1940 }
1941 };
Winson Chung785d2eb2011-04-14 16:08:02 -07001942 public static final Comparator<AppWidgetProviderInfo> WIDGET_NAME_COMPARATOR
1943 = new Comparator<AppWidgetProviderInfo>() {
1944 public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
1945 return sCollator.compare(a.label.toString(), b.label.toString());
1946 }
1947 };
Winson Chung5308f242011-08-18 12:12:41 -07001948 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
1949 if (info.activityInfo != null) {
1950 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
1951 } else {
1952 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
1953 }
1954 }
Winson Chung785d2eb2011-04-14 16:08:02 -07001955 public static class ShortcutNameComparator implements Comparator<ResolveInfo> {
1956 private PackageManager mPackageManager;
Winson Chungc3eecff2011-07-11 17:44:15 -07001957 private HashMap<Object, CharSequence> mLabelCache;
Winson Chung785d2eb2011-04-14 16:08:02 -07001958 ShortcutNameComparator(PackageManager pm) {
1959 mPackageManager = pm;
Winson Chungc3eecff2011-07-11 17:44:15 -07001960 mLabelCache = new HashMap<Object, CharSequence>();
1961 }
1962 ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) {
1963 mPackageManager = pm;
1964 mLabelCache = labelCache;
Winson Chung785d2eb2011-04-14 16:08:02 -07001965 }
1966 public final int compare(ResolveInfo a, ResolveInfo b) {
Winson Chungc3eecff2011-07-11 17:44:15 -07001967 CharSequence labelA, labelB;
Winson Chung5308f242011-08-18 12:12:41 -07001968 ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
1969 ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
1970 if (mLabelCache.containsKey(keyA)) {
1971 labelA = mLabelCache.get(keyA);
Winson Chungc3eecff2011-07-11 17:44:15 -07001972 } else {
1973 labelA = a.loadLabel(mPackageManager).toString();
1974
Winson Chung5308f242011-08-18 12:12:41 -07001975 mLabelCache.put(keyA, labelA);
Winson Chungc3eecff2011-07-11 17:44:15 -07001976 }
Winson Chung5308f242011-08-18 12:12:41 -07001977 if (mLabelCache.containsKey(keyB)) {
1978 labelB = mLabelCache.get(keyB);
Winson Chungc3eecff2011-07-11 17:44:15 -07001979 } else {
1980 labelB = b.loadLabel(mPackageManager).toString();
1981
Winson Chung5308f242011-08-18 12:12:41 -07001982 mLabelCache.put(keyB, labelB);
Winson Chungc3eecff2011-07-11 17:44:15 -07001983 }
Winson Chung785d2eb2011-04-14 16:08:02 -07001984 return sCollator.compare(labelA, labelB);
1985 }
1986 };
Winson Chung1ed747a2011-05-03 16:18:34 -07001987 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
1988 private PackageManager mPackageManager;
1989 private HashMap<Object, String> mLabelCache;
1990 WidgetAndShortcutNameComparator(PackageManager pm) {
1991 mPackageManager = pm;
1992 mLabelCache = new HashMap<Object, String>();
1993 }
1994 public final int compare(Object a, Object b) {
1995 String labelA, labelB;
Winson Chungc3eecff2011-07-11 17:44:15 -07001996 if (mLabelCache.containsKey(a)) {
1997 labelA = mLabelCache.get(a);
1998 } else {
1999 labelA = (a instanceof AppWidgetProviderInfo) ?
Winson Chung1ed747a2011-05-03 16:18:34 -07002000 ((AppWidgetProviderInfo) a).label :
2001 ((ResolveInfo) a).loadLabel(mPackageManager).toString();
Winson Chungc3eecff2011-07-11 17:44:15 -07002002 mLabelCache.put(a, labelA);
2003 }
2004 if (mLabelCache.containsKey(b)) {
2005 labelB = mLabelCache.get(b);
2006 } else {
2007 labelB = (b instanceof AppWidgetProviderInfo) ?
Winson Chung1ed747a2011-05-03 16:18:34 -07002008 ((AppWidgetProviderInfo) b).label :
2009 ((ResolveInfo) b).loadLabel(mPackageManager).toString();
Winson Chungc3eecff2011-07-11 17:44:15 -07002010 mLabelCache.put(b, labelB);
2011 }
Winson Chung1ed747a2011-05-03 16:18:34 -07002012 return sCollator.compare(labelA, labelB);
2013 }
2014 };
Joe Onoratobe386092009-11-17 17:32:16 -08002015
2016 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08002017 Log.d(TAG, "mCallbacks=" + mCallbacks);
2018 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mAllAppsList.data);
2019 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mAllAppsList.added);
2020 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mAllAppsList.removed);
2021 ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mAllAppsList.modified);
Joe Onorato36115782010-06-17 13:28:48 -04002022 if (mLoaderTask != null) {
2023 mLoaderTask.dumpState();
2024 } else {
2025 Log.d(TAG, "mLoaderTask=null");
2026 }
Joe Onoratobe386092009-11-17 17:32:16 -08002027 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002028}