blob: 460ab5db2f1ff206124626b7228d88e7f6f3c299 [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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
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;
Winson Chungc9168342013-06-26 14:54:55 -070022import android.content.*;
Joe Onorato0589f0f2010-02-08 13:44:00 -080023import android.content.Intent.ShortcutIconResource;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.pm.ActivityInfo;
Adam Cohen00fcb492011-11-02 21:53:47 -070025import android.content.pm.PackageInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080026import android.content.pm.PackageManager;
Adam Cohen00fcb492011-11-02 21:53:47 -070027import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.content.pm.ResolveInfo;
Reena Lee93f824a2011-09-23 17:20:28 -070029import android.content.res.Configuration;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.res.Resources;
31import android.database.Cursor;
32import android.graphics.Bitmap;
33import android.graphics.BitmapFactory;
34import android.net.Uri;
Joe Onorato17a89222011-02-08 17:26:11 -080035import android.os.Environment;
Joe Onorato36115782010-06-17 13:28:48 -040036import android.os.Handler;
37import android.os.HandlerThread;
Joe Onorato0589f0f2010-02-08 13:44:00 -080038import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.os.Process;
Winson Chungaafa03c2010-06-11 17:34:16 -070040import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040041import android.os.SystemClock;
Chris Wrenc3919c02013-09-18 09:48:33 -040042import android.provider.BaseColumns;
Winson Chunga90303b2013-11-15 13:05:06 -080043import android.text.TextUtils;
Winson Chungaafa03c2010-06-11 17:34:16 -070044import android.util.Log;
Winson Chungc9168342013-06-26 14:54:55 -070045import android.util.Pair;
Michael Jurka34c2e6c2013-12-13 16:07:45 +010046
Daniel Sandler325dc232013-06-05 22:57:57 -040047import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
Romain Guyedcce092010-03-04 13:03:17 -080048
Michael Jurkac2f801e2011-07-12 14:19:46 -070049import java.lang.ref.WeakReference;
50import java.net.URISyntaxException;
51import java.text.Collator;
52import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070053import java.util.Arrays;
Winson Chung64359a52013-07-08 17:17:08 -070054import java.util.Collection;
Michael Jurkac2f801e2011-07-12 14:19:46 -070055import java.util.Collections;
56import java.util.Comparator;
57import java.util.HashMap;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070058import java.util.HashSet;
Winson Chung2abf94d2012-07-18 18:16:38 -070059import java.util.Iterator;
Michael Jurkac2f801e2011-07-12 14:19:46 -070060import java.util.List;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070061import java.util.Set;
Adam Cohendcd297f2013-06-18 13:13:40 -070062import java.util.TreeMap;
Winson Chunga0b7e862013-09-05 16:03:15 -070063import java.util.concurrent.atomic.AtomicBoolean;
Michael Jurkac2f801e2011-07-12 14:19:46 -070064
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065/**
66 * Maintains in-memory state of the Launcher. It is expected that there should be only one
67 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070068 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069 */
Joe Onoratof99f8c12009-10-31 17:27:36 -040070public class LauncherModel extends BroadcastReceiver {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080071 static final boolean DEBUG_LOADERS = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -040072 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070073
Daniel Sandler8707e0f2013-08-15 15:54:18 -070074 // true = use a "More Apps" folder for non-workspace apps on upgrade
75 // false = strew non-workspace apps across the workspace on upgrade
76 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
77
Dan Sandlerd5024042014-01-09 15:01:33 -050078 public static final int LOADER_FLAG_NONE = 0;
79 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
80 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
81
Joe Onorato36115782010-06-17 13:28:48 -040082 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
Derek Prothro7aff3992013-12-10 14:00:37 -050083 private static final long INVALID_SCREEN_ID = -1L;
Winson Chungee055712013-07-30 14:46:24 -070084 private final boolean mAppsCanBeOnRemoveableStorage;
Daniel Sandlerdca66122010-04-13 16:23:58 -040085
Daniel Sandlercc8befa2013-06-11 14:45:48 -040086 private final LauncherAppState mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -040087 private final Object mLock = new Object();
88 private DeferredHandler mHandler = new DeferredHandler();
Joe Onorato36115782010-06-17 13:28:48 -040089 private LoaderTask mLoaderTask;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070090 private boolean mIsLoaderTaskRunning;
Michael Jurkac7700af2013-05-14 20:17:58 +020091 private volatile boolean mFlushingWorkerThread;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080092
Winson Chung81b52252012-08-27 15:34:29 -070093 // Specific runnable types that are run on the main thread deferred handler, this allows us to
94 // clear all queued binding runnables when the Launcher activity is destroyed.
95 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
96 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
97
98
Brad Fitzpatrick700889f2010-10-11 09:40:44 -070099 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
100 static {
101 sWorkerThread.start();
102 }
103 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
104
Joe Onoratocc67f472010-06-08 10:54:30 -0700105 // We start off with everything not loaded. After that, we assume that
106 // our monitoring of the package manager provides all updates and we never
107 // need to do a requery. These are only ever touched from the loader thread.
108 private boolean mWorkspaceLoaded;
109 private boolean mAllAppsLoaded;
110
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700111 // When we are loading pages synchronously, we can't just post the binding of items on the side
112 // pages as this delays the rotation process. Instead, we wait for a callback from the first
113 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
114 // a normal load, we also clear this set of Runnables.
115 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
116
Joe Onorato9c1289c2009-08-17 11:03:03 -0400117 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800118
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700119 // < only access in worker thread >
Adam Cohen4caf2982013-08-20 18:54:31 -0700120 AllAppsList mBgAllAppsList;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800121
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700122 // The lock that must be acquired before referencing any static bg data structures. Unlike
123 // other locks, this one can generally be held long-term because we never expect any of these
124 // static data structures to be referenced outside of the worker thread except on the first
125 // load after configuration change.
Winson Chung2abf94d2012-07-18 18:16:38 -0700126 static final Object sBgLock = new Object();
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700127
Adam Cohen487f7dd2012-06-28 18:12:10 -0700128 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700129 // LauncherModel to their ids
Adam Cohen487f7dd2012-06-28 18:12:10 -0700130 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700131
Adam Cohen487f7dd2012-06-28 18:12:10 -0700132 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
133 // created by LauncherModel that are directly on the home screen (however, no widgets or
134 // shortcuts within folders).
135 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700136
Adam Cohen487f7dd2012-06-28 18:12:10 -0700137 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
138 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700139 new ArrayList<LauncherAppWidgetInfo>();
140
Adam Cohen487f7dd2012-06-28 18:12:10 -0700141 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
142 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
Winson Chungb1094bd2011-08-24 16:14:08 -0700143
Adam Cohen487f7dd2012-06-28 18:12:10 -0700144 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
145 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
Adam Cohendcd297f2013-06-18 13:13:40 -0700146
147 // sBgWorkspaceScreens is the ordered set of workspace screens.
148 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
149
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700150 // </ only access in worker thread >
151
152 private IconCache mIconCache;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800153 private Bitmap mDefaultIcon;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800154
Reena Lee99a73f32011-10-24 17:27:37 -0700155 protected int mPreviousConfigMcc;
Reena Lee93f824a2011-09-23 17:20:28 -0700156
Joe Onorato9c1289c2009-08-17 11:03:03 -0400157 public interface Callbacks {
Joe Onoratoef2efcf2010-10-27 13:21:00 -0700158 public boolean setLoadOnResume();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400159 public int getCurrentWorkspaceScreen();
160 public void startBinding();
Winson Chung64359a52013-07-08 17:17:08 -0700161 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
162 boolean forceAnimateIcons);
Adam Cohendcd297f2013-06-18 13:13:40 -0700163 public void bindScreens(ArrayList<Long> orderedScreenIds);
Winson Chung64359a52013-07-08 17:17:08 -0700164 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
Joe Onoratoad72e172009-11-06 16:25:04 -0500165 public void bindFolders(HashMap<Long,FolderInfo> folders);
Adam Cohene25af792013-06-06 23:08:25 -0700166 public void finishBindingItems(boolean upgradePath);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400167 public void bindAppWidget(LauncherAppWidgetInfo info);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200168 public void bindAllApplications(ArrayList<AppInfo> apps);
Winson Chungd64d1762013-08-20 14:37:16 -0700169 public void bindAppsAdded(ArrayList<Long> newScreens,
170 ArrayList<ItemInfo> addNotAnimated,
Winson Chungc58497e2013-09-03 17:48:37 -0700171 ArrayList<ItemInfo> addAnimated,
172 ArrayList<AppInfo> addedApps);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200173 public void bindAppsUpdated(ArrayList<AppInfo> apps);
Winson Chung83892cc2013-05-01 16:53:33 -0700174 public void bindComponentsRemoved(ArrayList<String> packageNames,
Winson Chungdf95eb12013-10-16 14:57:07 -0700175 ArrayList<AppInfo> appInfos);
Michael Jurkac402cd92013-05-20 15:49:32 +0200176 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
Narayan Kamathcb1a4772011-06-28 13:46:59 +0100177 public void bindSearchablesChanged();
Winson Chunga0b7e862013-09-05 16:03:15 -0700178 public boolean isAllAppsButtonRank(int rank);
Adam Cohen1462de32012-07-24 22:34:36 -0700179 public void onPageBoundSynchronously(int page);
Winson Chungede41292013-09-19 16:27:36 -0700180 public void dumpLogsToLocalData();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400181 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800182
Winson Chung64359a52013-07-08 17:17:08 -0700183 public interface ItemInfoFilter {
184 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
185 }
186
Bjorn Bringert1307f632013-10-03 22:31:03 +0100187 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400188 final Context context = app.getContext();
189
Winson Chungee055712013-07-30 14:46:24 -0700190 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
Daniel Sandlere4f98912013-06-25 15:13:26 -0400191 mApp = app;
Bjorn Bringert1307f632013-10-03 22:31:03 +0100192 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800193 mIconCache = iconCache;
194
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400195 final Resources res = context.getResources();
Reena Lee99a73f32011-10-24 17:27:37 -0700196 Configuration config = res.getConfiguration();
197 mPreviousConfigMcc = config.mcc;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800198 }
199
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700200 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
201 * posted on the main thread handler. */
202 private void runOnMainThread(Runnable r) {
Winson Chung81b52252012-08-27 15:34:29 -0700203 runOnMainThread(r, 0);
204 }
205 private void runOnMainThread(Runnable r, int type) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700206 if (sWorkerThread.getThreadId() == Process.myTid()) {
207 // If we are on the worker thread, post onto the main handler
208 mHandler.post(r);
209 } else {
210 r.run();
211 }
212 }
213
214 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
215 * posted on the worker thread handler. */
216 private static void runOnWorkerThread(Runnable r) {
217 if (sWorkerThread.getThreadId() == Process.myTid()) {
218 r.run();
219 } else {
220 // If we are not on the worker thread, then post to the worker handler
221 sWorker.post(r);
222 }
223 }
224
Winson Chungc9168342013-06-26 14:54:55 -0700225 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
226 long screen) {
Winson Chung892c74d2013-08-22 16:15:50 -0700227 LauncherAppState app = LauncherAppState.getInstance();
228 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
229 final int xCount = (int) grid.numColumns;
230 final int yCount = (int) grid.numRows;
Winson Chungc9168342013-06-26 14:54:55 -0700231 boolean[][] occupied = new boolean[xCount][yCount];
232
233 int cellX, cellY, spanX, spanY;
234 for (int i = 0; i < items.size(); ++i) {
235 final ItemInfo item = items.get(i);
236 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
237 if (item.screenId == screen) {
238 cellX = item.cellX;
239 cellY = item.cellY;
240 spanX = item.spanX;
241 spanY = item.spanY;
242 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
243 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
244 occupied[x][y] = true;
245 }
246 }
247 }
248 }
249 }
250
251 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
252 }
253 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
Winson Chung156ab5b2013-07-12 14:14:16 -0700254 Intent launchIntent,
Winson Chung76828c82013-08-19 15:43:29 -0700255 int firstScreenIndex,
256 ArrayList<Long> workspaceScreens) {
Winson Chungc9168342013-06-26 14:54:55 -0700257 // Lock on the app so that we don't try and get the items while apps are being added
258 LauncherAppState app = LauncherAppState.getInstance();
259 LauncherModel model = app.getModel();
260 boolean found = false;
261 synchronized (app) {
Winson Chung64359a52013-07-08 17:17:08 -0700262 if (sWorkerThread.getThreadId() != Process.myTid()) {
263 // Flush the LauncherModel worker thread, so that if we just did another
264 // processInstallShortcut, we give it time for its shortcut to get added to the
265 // database (getItemsInLocalCoordinates reads the database)
266 model.flushWorkerThread();
267 }
Winson Chungc9168342013-06-26 14:54:55 -0700268 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
Winson Chungc9168342013-06-26 14:54:55 -0700269
270 // Try adding to the workspace screens incrementally, starting at the default or center
271 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
Winson Chung76828c82013-08-19 15:43:29 -0700272 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
273 int count = workspaceScreens.size();
Winson Chung156ab5b2013-07-12 14:14:16 -0700274 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
Winson Chungc9168342013-06-26 14:54:55 -0700275 int[] tmpCoordinates = new int[2];
276 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
Winson Chung76828c82013-08-19 15:43:29 -0700277 workspaceScreens.get(screen))) {
Winson Chungc9168342013-06-26 14:54:55 -0700278 // Update the Launcher db
Winson Chung76828c82013-08-19 15:43:29 -0700279 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
Winson Chungc9168342013-06-26 14:54:55 -0700280 }
281 }
282 }
Winson Chungc9168342013-06-26 14:54:55 -0700283 return null;
284 }
285
Winson Chung94d67682013-09-25 16:29:40 -0700286 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
287 final ArrayList<AppInfo> allAppsApps) {
Winson Chung997a9232013-07-24 15:33:46 -0700288 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Winson Chung94d67682013-09-25 16:29:40 -0700289 addAndBindAddedApps(context, workspaceApps, cb, allAppsApps);
Winson Chung997a9232013-07-24 15:33:46 -0700290 }
Winson Chung94d67682013-09-25 16:29:40 -0700291 public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
Winson Chungfe9d96a2013-11-14 11:30:05 -0800292 final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) {
293 if (workspaceApps == null || allAppsApps == null) {
294 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
295 }
Winson Chung94d67682013-09-25 16:29:40 -0700296 if (workspaceApps.isEmpty() && allAppsApps.isEmpty()) {
Winson Chung9e6a0a22013-08-27 11:58:12 -0700297 return;
Winson Chung997a9232013-07-24 15:33:46 -0700298 }
Winson Chung64359a52013-07-08 17:17:08 -0700299 // Process the newly added applications and add them to the database first
300 Runnable r = new Runnable() {
301 public void run() {
302 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
303 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
304
Winson Chung76828c82013-08-19 15:43:29 -0700305 // Get the list of workspace screens. We need to append to this list and
306 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
307 // called.
308 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
309 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
310 for (Integer i : orderedScreens.keySet()) {
311 long screenId = orderedScreens.get(i);
312 workspaceScreens.add(screenId);
313 }
314
Winson Chung64359a52013-07-08 17:17:08 -0700315 synchronized(sBgLock) {
Winson Chung94d67682013-09-25 16:29:40 -0700316 Iterator<ItemInfo> iter = workspaceApps.iterator();
Winson Chung64359a52013-07-08 17:17:08 -0700317 while (iter.hasNext()) {
Winson Chung997a9232013-07-24 15:33:46 -0700318 ItemInfo a = iter.next();
Winson Chung64359a52013-07-08 17:17:08 -0700319 final String name = a.title.toString();
Winson Chung997a9232013-07-24 15:33:46 -0700320 final Intent launchIntent = a.getIntent();
Winson Chung64359a52013-07-08 17:17:08 -0700321
322 // Short-circuit this logic if the icon exists somewhere on the workspace
323 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
324 continue;
325 }
326
Winson Chung87412982013-10-03 18:34:14 -0700327 // Add this icon to the db, creating a new page if necessary. If there
328 // is only the empty page then we just add items to the first page.
329 // Otherwise, we add them to the next pages.
330 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
Winson Chung64359a52013-07-08 17:17:08 -0700331 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
Winson Chung76828c82013-08-19 15:43:29 -0700332 name, launchIntent, startSearchPageIndex, workspaceScreens);
Winson Chung64359a52013-07-08 17:17:08 -0700333 if (coords == null) {
Michael Jurka414300a2013-08-27 15:42:35 +0200334 LauncherProvider lp = LauncherAppState.getLauncherProvider();
Winson Chungc763c4e2013-07-19 13:49:06 -0700335
336 // If we can't find a valid position, then just add a new screen.
337 // This takes time so we need to re-queue the add until the new
338 // page is added. Create as many screens as necessary to satisfy
339 // the startSearchPageIndex.
340 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
Winson Chung76828c82013-08-19 15:43:29 -0700341 workspaceScreens.size());
Winson Chungc763c4e2013-07-19 13:49:06 -0700342 while (numPagesToAdd > 0) {
343 long screenId = lp.generateNewScreenId();
Winson Chungc763c4e2013-07-19 13:49:06 -0700344 // Save the screen id for binding in the workspace
Winson Chung76828c82013-08-19 15:43:29 -0700345 workspaceScreens.add(screenId);
Winson Chungc763c4e2013-07-19 13:49:06 -0700346 addedWorkspaceScreensFinal.add(screenId);
347 numPagesToAdd--;
348 }
Winson Chung76828c82013-08-19 15:43:29 -0700349
Winson Chung64359a52013-07-08 17:17:08 -0700350 // Find the coordinate again
351 coords = LauncherModel.findNextAvailableIconSpace(context,
Winson Chung76828c82013-08-19 15:43:29 -0700352 name, launchIntent, startSearchPageIndex, workspaceScreens);
Winson Chung64359a52013-07-08 17:17:08 -0700353 }
354 if (coords == null) {
355 throw new RuntimeException("Coordinates should not be null");
356 }
357
Winson Chung997a9232013-07-24 15:33:46 -0700358 ShortcutInfo shortcutInfo;
359 if (a instanceof ShortcutInfo) {
360 shortcutInfo = (ShortcutInfo) a;
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200361 } else if (a instanceof AppInfo) {
362 shortcutInfo = ((AppInfo) a).makeShortcut();
Winson Chung997a9232013-07-24 15:33:46 -0700363 } else {
364 throw new RuntimeException("Unexpected info type");
365 }
Winson Chung94d67682013-09-25 16:29:40 -0700366
Winson Chung64359a52013-07-08 17:17:08 -0700367 // Add the shortcut to the db
368 addItemToDatabase(context, shortcutInfo,
369 LauncherSettings.Favorites.CONTAINER_DESKTOP,
370 coords.first, coords.second[0], coords.second[1], false);
371 // Save the ShortcutInfo for binding in the workspace
372 addedShortcutsFinal.add(shortcutInfo);
373 }
374 }
375
Winson Chung76828c82013-08-19 15:43:29 -0700376 // Update the workspace screens
377 updateWorkspaceScreenOrder(context, workspaceScreens);
378
Winson Chung94d67682013-09-25 16:29:40 -0700379 if (!addedShortcutsFinal.isEmpty() || !allAppsApps.isEmpty()) {
Winson Chung997a9232013-07-24 15:33:46 -0700380 runOnMainThread(new Runnable() {
381 public void run() {
382 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
383 if (callbacks == cb && cb != null) {
Winson Chung997a9232013-07-24 15:33:46 -0700384 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
385 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
Winson Chung94d67682013-09-25 16:29:40 -0700386 if (!addedShortcutsFinal.isEmpty()) {
387 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
388 long lastScreenId = info.screenId;
389 for (ItemInfo i : addedShortcutsFinal) {
390 if (i.screenId == lastScreenId) {
391 addAnimated.add(i);
392 } else {
393 addNotAnimated.add(i);
394 }
Winson Chung997a9232013-07-24 15:33:46 -0700395 }
396 }
Winson Chungd64d1762013-08-20 14:37:16 -0700397 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
Winson Chung94d67682013-09-25 16:29:40 -0700398 addNotAnimated, addAnimated, allAppsApps);
Winson Chung997a9232013-07-24 15:33:46 -0700399 }
Winson Chung64359a52013-07-08 17:17:08 -0700400 }
Winson Chung997a9232013-07-24 15:33:46 -0700401 });
402 }
Winson Chung64359a52013-07-08 17:17:08 -0700403 }
404 };
405 runOnWorkerThread(r);
406 }
407
Joe Onorato56d82912010-03-07 14:32:10 -0500408 public Bitmap getFallbackIcon() {
Winson Chung5801ef02013-10-16 13:46:28 -0700409 if (mDefaultIcon == null) {
410 final Context context = LauncherAppState.getInstance().getContext();
411 mDefaultIcon = Utilities.createIconBitmap(
412 mIconCache.getFullResDefaultActivityIcon(), context);
413 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800414 return Bitmap.createBitmap(mDefaultIcon);
Joe Onoratof99f8c12009-10-31 17:27:36 -0400415 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800416
Winson Chung81b52252012-08-27 15:34:29 -0700417 public void unbindItemInfosAndClearQueuedBindRunnables() {
418 if (sWorkerThread.getThreadId() == Process.myTid()) {
419 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
420 "main thread");
421 }
422
423 // Clear any deferred bind runnables
424 mDeferredBindRunnables.clear();
425 // Remove any queued bind runnables
426 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
427 // Unbind all the workspace items
428 unbindWorkspaceItemsOnMainThread();
Winson Chung603bcb92011-09-02 11:45:39 -0700429 }
430
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700431 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
Winson Chung81b52252012-08-27 15:34:29 -0700432 void unbindWorkspaceItemsOnMainThread() {
Winson Chung603bcb92011-09-02 11:45:39 -0700433 // Ensure that we don't use the same workspace items data structure on the main thread
434 // by making a copy of workspace items first.
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700435 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
436 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
Winson Chung2abf94d2012-07-18 18:16:38 -0700437 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700438 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
439 tmpAppWidgets.addAll(sBgAppWidgets);
440 }
441 Runnable r = new Runnable() {
442 @Override
443 public void run() {
444 for (ItemInfo item : tmpWorkspaceItems) {
445 item.unbind();
446 }
447 for (ItemInfo item : tmpAppWidgets) {
448 item.unbind();
449 }
450 }
451 };
452 runOnMainThread(r);
Adam Cohen4eac29a2011-07-11 17:53:37 -0700453 }
454
Joe Onorato9c1289c2009-08-17 11:03:03 -0400455 /**
456 * Adds an item to the DB if it was not created previously, or move it to a new
457 * <container, screen, cellX, cellY>
458 */
459 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700460 long screenId, int cellX, int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400461 if (item.container == ItemInfo.NO_ID) {
462 // From all apps
Adam Cohendcd297f2013-06-18 13:13:40 -0700463 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400464 } else {
465 // From somewhere else
Adam Cohendcd297f2013-06-18 13:13:40 -0700466 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800467 }
468 }
469
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700470 static void checkItemInfoLocked(
471 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
472 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
473 if (modelItem != null && item != modelItem) {
474 // check all the data is consistent
475 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
476 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
477 ShortcutInfo shortcut = (ShortcutInfo) item;
478 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
479 modelShortcut.intent.filterEquals(shortcut.intent) &&
480 modelShortcut.id == shortcut.id &&
481 modelShortcut.itemType == shortcut.itemType &&
482 modelShortcut.container == shortcut.container &&
Adam Cohendcd297f2013-06-18 13:13:40 -0700483 modelShortcut.screenId == shortcut.screenId &&
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700484 modelShortcut.cellX == shortcut.cellX &&
485 modelShortcut.cellY == shortcut.cellY &&
486 modelShortcut.spanX == shortcut.spanX &&
487 modelShortcut.spanY == shortcut.spanY &&
488 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
489 (modelShortcut.dropPos != null &&
490 shortcut.dropPos != null &&
491 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
492 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
493 // For all intents and purposes, this is the same object
494 return;
495 }
496 }
497
498 // the modelItem needs to match up perfectly with item if our model is
499 // to be consistent with the database-- for now, just require
500 // modelItem == item or the equality check above
501 String msg = "item: " + ((item != null) ? item.toString() : "null") +
502 "modelItem: " +
503 ((modelItem != null) ? modelItem.toString() : "null") +
504 "Error: ItemInfo passed to checkItemInfo doesn't match original";
505 RuntimeException e = new RuntimeException(msg);
506 if (stackTrace != null) {
507 e.setStackTrace(stackTrace);
508 }
Adam Cohenb9ada652013-11-08 08:25:08 -0800509 throw e;
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700510 }
511 }
512
Michael Jurka816474f2012-06-25 14:49:02 -0700513 static void checkItemInfo(final ItemInfo item) {
514 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
515 final long itemId = item.id;
516 Runnable r = new Runnable() {
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700517 public void run() {
518 synchronized (sBgLock) {
519 checkItemInfoLocked(itemId, item, stackTrace);
Michael Jurka816474f2012-06-25 14:49:02 -0700520 }
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700521 }
522 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700523 runOnWorkerThread(r);
Michael Jurka816474f2012-06-25 14:49:02 -0700524 }
525
Michael Jurkac9d95c52011-08-29 14:03:34 -0700526 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
527 final ItemInfo item, final String callingFunction) {
528 final long itemId = item.id;
529 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
530 final ContentResolver cr = context.getContentResolver();
531
Adam Cohen487f7dd2012-06-28 18:12:10 -0700532 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700533 Runnable r = new Runnable() {
534 public void run() {
535 cr.update(uri, values, null, null);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700536 updateItemArrays(item, itemId, stackTrace);
537 }
538 };
539 runOnWorkerThread(r);
540 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700541
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700542 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
543 final ArrayList<ItemInfo> items, final String callingFunction) {
544 final ContentResolver cr = context.getContentResolver();
Adam Cohen487f7dd2012-06-28 18:12:10 -0700545
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700546 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
547 Runnable r = new Runnable() {
548 public void run() {
549 ArrayList<ContentProviderOperation> ops =
550 new ArrayList<ContentProviderOperation>();
551 int count = items.size();
552 for (int i = 0; i < count; i++) {
553 ItemInfo item = items.get(i);
554 final long itemId = item.id;
555 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
556 ContentValues values = valuesList.get(i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700557
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700558 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
559 updateItemArrays(item, itemId, stackTrace);
560
561 }
562 try {
563 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
564 } catch (Exception e) {
565 e.printStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700566 }
567 }
568 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700569 runOnWorkerThread(r);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700570 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700571
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700572 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
573 // Lock on mBgLock *after* the db operation
574 synchronized (sBgLock) {
575 checkItemInfoLocked(itemId, item, stackTrace);
576
577 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
578 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
579 // Item is in a folder, make sure this folder exists
580 if (!sBgFolders.containsKey(item.container)) {
581 // An items container is being set to a that of an item which is not in
582 // the list of Folders.
583 String msg = "item: " + item + " container being set to: " +
584 item.container + ", not in the list of folders";
585 Log.e(TAG, msg);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700586 }
587 }
588
589 // Items are added/removed from the corresponding FolderInfo elsewhere, such
590 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
591 // that are on the desktop, as appropriate
592 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
Winson Chung33231f52013-12-09 16:57:45 -0800593 if (modelItem != null &&
594 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
595 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700596 switch (modelItem.itemType) {
597 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
598 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
599 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
600 if (!sBgWorkspaceItems.contains(modelItem)) {
601 sBgWorkspaceItems.add(modelItem);
602 }
603 break;
604 default:
605 break;
606 }
607 } else {
608 sBgWorkspaceItems.remove(modelItem);
609 }
610 }
611 }
612
Michael Jurkac7700af2013-05-14 20:17:58 +0200613 public void flushWorkerThread() {
614 mFlushingWorkerThread = true;
615 Runnable waiter = new Runnable() {
616 public void run() {
617 synchronized (this) {
618 notifyAll();
619 mFlushingWorkerThread = false;
620 }
621 }
622 };
623
624 synchronized(waiter) {
625 runOnWorkerThread(waiter);
626 if (mLoaderTask != null) {
627 synchronized(mLoaderTask) {
628 mLoaderTask.notify();
629 }
630 }
631 boolean success = false;
632 while (!success) {
633 try {
634 waiter.wait();
635 success = true;
636 } catch (InterruptedException e) {
637 }
638 }
639 }
640 }
641
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800642 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400643 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700644 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700645 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700646 final long screenId, final int cellX, final int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400647 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400648 item.cellX = cellX;
649 item.cellY = cellY;
Michael Jurkac9d95c52011-08-29 14:03:34 -0700650
Winson Chung3d503fb2011-07-13 17:25:49 -0700651 // We store hotseat items in canonical form which is this orientation invariant position
652 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700653 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -0700654 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700655 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -0700656 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700657 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -0700658 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400659
660 final ContentValues values = new ContentValues();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400661 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Winson Chung3d503fb2011-07-13 17:25:49 -0700662 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
663 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700664 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400665
Michael Jurkac9d95c52011-08-29 14:03:34 -0700666 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700667 }
668
669 /**
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700670 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
671 * cellX, cellY have already been updated on the ItemInfos.
672 */
673 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
674 final long container, final int screen) {
675
676 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
677 int count = items.size();
678
679 for (int i = 0; i < count; i++) {
680 ItemInfo item = items.get(i);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700681 item.container = container;
682
683 // We store hotseat items in canonical form which is this orientation invariant position
684 // in the hotseat
685 if (context instanceof Launcher && screen < 0 &&
686 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700687 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700688 item.cellY);
689 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700690 item.screenId = screen;
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700691 }
692
693 final ContentValues values = new ContentValues();
694 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
695 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
696 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700697 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700698
699 contentValues.add(values);
700 }
701 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
702 }
703
704 /**
Adam Cohenbebf0422012-04-11 18:06:28 -0700705 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
Adam Cohend4844c32011-02-18 19:25:06 -0800706 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700707 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700708 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
Winson Chung0f84a602013-09-30 14:30:58 -0700709 item.container = container;
Adam Cohend4844c32011-02-18 19:25:06 -0800710 item.cellX = cellX;
711 item.cellY = cellY;
Adam Cohenbebf0422012-04-11 18:06:28 -0700712 item.spanX = spanX;
713 item.spanY = spanY;
714
715 // We store hotseat items in canonical form which is this orientation invariant position
716 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700717 if (context instanceof Launcher && screenId < 0 &&
Adam Cohenbebf0422012-04-11 18:06:28 -0700718 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700719 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Adam Cohenbebf0422012-04-11 18:06:28 -0700720 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700721 item.screenId = screenId;
Adam Cohenbebf0422012-04-11 18:06:28 -0700722 }
Adam Cohend4844c32011-02-18 19:25:06 -0800723
Adam Cohend4844c32011-02-18 19:25:06 -0800724 final ContentValues values = new ContentValues();
Adam Cohend4844c32011-02-18 19:25:06 -0800725 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Adam Cohenbebf0422012-04-11 18:06:28 -0700726 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
727 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
728 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
729 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700730 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohend4844c32011-02-18 19:25:06 -0800731
Michael Jurka816474f2012-06-25 14:49:02 -0700732 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
Adam Cohenbebf0422012-04-11 18:06:28 -0700733 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700734
735 /**
736 * Update an item to the database in a specified container.
737 */
738 static void updateItemInDatabase(Context context, final ItemInfo item) {
739 final ContentValues values = new ContentValues();
740 item.onAddToDatabase(values);
741 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
742 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
Adam Cohend4844c32011-02-18 19:25:06 -0800743 }
744
745 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400746 * Returns true if the shortcuts already exists in the database.
747 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800748 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400749 static boolean shortcutExists(Context context, String title, Intent intent) {
750 final ContentResolver cr = context.getContentResolver();
751 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
752 new String[] { "title", "intent" }, "title=? and intent=?",
753 new String[] { title, intent.toUri(0) }, null);
754 boolean result = false;
755 try {
756 result = c.moveToFirst();
757 } finally {
758 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800759 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400760 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700761 }
762
Joe Onorato9c1289c2009-08-17 11:03:03 -0400763 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700764 * Returns an ItemInfo array containing all the items in the LauncherModel.
765 * The ItemInfo.id is not set through this function.
766 */
767 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
768 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
769 final ContentResolver cr = context.getContentResolver();
770 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
771 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
772 LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
773 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
774
775 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
776 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
777 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
778 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
779 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
780 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
781 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
782
783 try {
784 while (c.moveToNext()) {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700785 ItemInfo item = new ItemInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -0700786 item.cellX = c.getInt(cellXIndex);
787 item.cellY = c.getInt(cellYIndex);
Winson Chung61c69862013-08-21 19:10:29 -0700788 item.spanX = Math.max(1, c.getInt(spanXIndex));
789 item.spanY = Math.max(1, c.getInt(spanYIndex));
Winson Chungaafa03c2010-06-11 17:34:16 -0700790 item.container = c.getInt(containerIndex);
791 item.itemType = c.getInt(itemTypeIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700792 item.screenId = c.getInt(screenIndex);
Winson Chungaafa03c2010-06-11 17:34:16 -0700793
794 items.add(item);
795 }
796 } catch (Exception e) {
797 items.clear();
798 } finally {
799 c.close();
800 }
801
802 return items;
803 }
804
805 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400806 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
807 */
808 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
809 final ContentResolver cr = context.getContentResolver();
810 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
811 "_id=? and (itemType=? or itemType=?)",
812 new String[] { String.valueOf(id),
Adam Cohendf2cc412011-04-27 16:56:57 -0700813 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700814
Joe Onorato9c1289c2009-08-17 11:03:03 -0400815 try {
816 if (c.moveToFirst()) {
817 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
818 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
819 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
820 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
821 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
822 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800823
Joe Onorato9c1289c2009-08-17 11:03:03 -0400824 FolderInfo folderInfo = null;
825 switch (c.getInt(itemTypeIndex)) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700826 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
827 folderInfo = findOrMakeFolder(folderList, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400828 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700829 }
830
Joe Onorato9c1289c2009-08-17 11:03:03 -0400831 folderInfo.title = c.getString(titleIndex);
832 folderInfo.id = id;
833 folderInfo.container = c.getInt(containerIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700834 folderInfo.screenId = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700835 folderInfo.cellX = c.getInt(cellXIndex);
836 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400837
838 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700839 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400840 } finally {
841 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700842 }
843
844 return null;
845 }
846
Joe Onorato9c1289c2009-08-17 11:03:03 -0400847 /**
848 * Add an item to the database in a specified container. Sets the container, screen, cellX and
849 * cellY fields of the item. Also assigns an ID to the item.
850 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700851 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700852 final long screenId, final int cellX, final int cellY, final boolean notify) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400853 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400854 item.cellX = cellX;
855 item.cellY = cellY;
Winson Chung3d503fb2011-07-13 17:25:49 -0700856 // We store hotseat items in canonical form which is this orientation invariant position
857 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700858 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -0700859 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700860 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -0700861 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700862 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -0700863 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400864
865 final ContentValues values = new ContentValues();
866 final ContentResolver cr = context.getContentResolver();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400867 item.onAddToDatabase(values);
868
Michael Jurka414300a2013-08-27 15:42:35 +0200869 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700870 values.put(LauncherSettings.Favorites._ID, item.id);
Winson Chung3d503fb2011-07-13 17:25:49 -0700871 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700872
Michael Jurkac9d95c52011-08-29 14:03:34 -0700873 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700874 public void run() {
875 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
876 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400877
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700878 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -0700879 synchronized (sBgLock) {
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700880 checkItemInfoLocked(item.id, item, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700881 sBgItemsIdMap.put(item.id, item);
882 switch (item.itemType) {
883 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
884 sBgFolders.put(item.id, (FolderInfo) item);
885 // Fall through
886 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
887 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
888 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
889 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
890 sBgWorkspaceItems.add(item);
891 } else {
892 if (!sBgFolders.containsKey(item.container)) {
893 // Adding an item to a folder that doesn't exist.
894 String msg = "adding item: " + item + " to a folder that " +
895 " doesn't exist";
Adam Cohen28b3e102012-10-04 17:21:33 -0700896 Log.e(TAG, msg);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700897 }
Adam Cohen487f7dd2012-06-28 18:12:10 -0700898 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700899 break;
900 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
901 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
902 break;
903 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700904 }
905 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700906 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700907 runOnWorkerThread(r);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700908 }
909
Joe Onorato9c1289c2009-08-17 11:03:03 -0400910 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700911 * Creates a new unique child id, for a given cell span across all layouts.
912 */
Michael Jurka845ba3b2010-09-28 17:09:46 -0700913 static int getCellLayoutChildId(
Adam Cohendcd297f2013-06-18 13:13:40 -0700914 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700915 return (((int) container & 0xFF) << 24)
Adam Cohendcd297f2013-06-18 13:13:40 -0700916 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
Winson Chungaafa03c2010-06-11 17:34:16 -0700917 }
918
Winson Chungaafa03c2010-06-11 17:34:16 -0700919 /**
Michael Jurkac9d95c52011-08-29 14:03:34 -0700920 * Removes the specified item from the database
921 * @param context
922 * @param item
Joe Onorato9c1289c2009-08-17 11:03:03 -0400923 */
Michael Jurkac9d95c52011-08-29 14:03:34 -0700924 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400925 final ContentResolver cr = context.getContentResolver();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700926 final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
Adam Cohen487f7dd2012-06-28 18:12:10 -0700927
Michael Jurka83df1882011-08-31 20:59:26 -0700928 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700929 public void run() {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700930 cr.delete(uriToDelete, null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700931
932 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -0700933 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700934 switch (item.itemType) {
935 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
936 sBgFolders.remove(item.id);
937 for (ItemInfo info: sBgItemsIdMap.values()) {
938 if (info.container == item.id) {
939 // We are deleting a folder which still contains items that
940 // think they are contained by that folder.
941 String msg = "deleting a folder (" + item + ") which still " +
942 "contains items (" + info + ")";
Adam Cohen28b3e102012-10-04 17:21:33 -0700943 Log.e(TAG, msg);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700944 }
Adam Cohen487f7dd2012-06-28 18:12:10 -0700945 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700946 sBgWorkspaceItems.remove(item);
947 break;
948 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
949 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
950 sBgWorkspaceItems.remove(item);
951 break;
952 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
953 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
954 break;
955 }
956 sBgItemsIdMap.remove(item.id);
957 sBgDbIconCache.remove(item);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700958 }
959 }
Michael Jurka83df1882011-08-31 20:59:26 -0700960 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700961 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400962 }
963
964 /**
Adam Cohendcd297f2013-06-18 13:13:40 -0700965 * Update the order of the workspace screens in the database. The array list contains
966 * a list of screen ids in the order that they should appear.
967 */
Winson Chungc9168342013-06-26 14:54:55 -0700968 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800969 // Log to disk
970 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
971 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
972
Winson Chung64359a52013-07-08 17:17:08 -0700973 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
Adam Cohendcd297f2013-06-18 13:13:40 -0700974 final ContentResolver cr = context.getContentResolver();
975 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
976
977 // Remove any negative screen ids -- these aren't persisted
Winson Chung64359a52013-07-08 17:17:08 -0700978 Iterator<Long> iter = screensCopy.iterator();
Adam Cohendcd297f2013-06-18 13:13:40 -0700979 while (iter.hasNext()) {
980 long id = iter.next();
981 if (id < 0) {
982 iter.remove();
983 }
984 }
985
986 Runnable r = new Runnable() {
987 @Override
988 public void run() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700989 // Clear the table
990 cr.delete(uri, null, null);
Winson Chung76828c82013-08-19 15:43:29 -0700991 int count = screensCopy.size();
Adam Cohendcd297f2013-06-18 13:13:40 -0700992 ContentValues[] values = new ContentValues[count];
993 for (int i = 0; i < count; i++) {
994 ContentValues v = new ContentValues();
Winson Chung76828c82013-08-19 15:43:29 -0700995 long screenId = screensCopy.get(i);
Adam Cohendcd297f2013-06-18 13:13:40 -0700996 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
997 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
Adam Cohendcd297f2013-06-18 13:13:40 -0700998 values[i] = v;
999 }
1000 cr.bulkInsert(uri, values);
Winson Chung9e6a0a22013-08-27 11:58:12 -07001001
Winson Chungba9c37f2013-08-30 14:11:37 -07001002 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001003 sBgWorkspaceScreens.clear();
1004 sBgWorkspaceScreens.addAll(screensCopy);
Adam Cohen4caf2982013-08-20 18:54:31 -07001005 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001006 }
1007 };
1008 runOnWorkerThread(r);
1009 }
1010
1011 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012 * Remove the contents of the specified folder from the database
1013 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001014 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001015 final ContentResolver cr = context.getContentResolver();
1016
Michael Jurkac9d95c52011-08-29 14:03:34 -07001017 Runnable r = new Runnable() {
1018 public void run() {
1019 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001020 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001021 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001022 sBgItemsIdMap.remove(info.id);
1023 sBgFolders.remove(info.id);
1024 sBgDbIconCache.remove(info);
1025 sBgWorkspaceItems.remove(info);
1026 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001027
Michael Jurkac9d95c52011-08-29 14:03:34 -07001028 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1029 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001030 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001031 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001032 for (ItemInfo childInfo : info.contents) {
1033 sBgItemsIdMap.remove(childInfo.id);
1034 sBgDbIconCache.remove(childInfo);
1035 }
Adam Cohenafb01ee2011-06-23 15:38:03 -07001036 }
Michael Jurkac9d95c52011-08-29 14:03:34 -07001037 }
1038 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001039 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001040 }
1041
1042 /**
1043 * Set this as the current Launcher activity object for the loader.
1044 */
1045 public void initialize(Callbacks callbacks) {
1046 synchronized (mLock) {
1047 mCallbacks = new WeakReference<Callbacks>(callbacks);
1048 }
1049 }
1050
Joe Onorato1d8e7bb2009-10-15 19:49:43 -07001051 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001052 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1053 * ACTION_PACKAGE_CHANGED.
1054 */
Narayan Kamathcb1a4772011-06-28 13:46:59 +01001055 @Override
Joe Onoratof99f8c12009-10-31 17:27:36 -04001056 public void onReceive(Context context, Intent intent) {
Joe Onorato36115782010-06-17 13:28:48 -04001057 if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
Winson Chungaafa03c2010-06-11 17:34:16 -07001058
Joe Onorato36115782010-06-17 13:28:48 -04001059 final String action = intent.getAction();
Joe Onoratof99f8c12009-10-31 17:27:36 -04001060
Joe Onorato36115782010-06-17 13:28:48 -04001061 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
1062 || Intent.ACTION_PACKAGE_REMOVED.equals(action)
1063 || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1064 final String packageName = intent.getData().getSchemeSpecificPart();
1065 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001066
Joe Onorato36115782010-06-17 13:28:48 -04001067 int op = PackageUpdatedTask.OP_NONE;
1068
1069 if (packageName == null || packageName.length() == 0) {
1070 // they sent us a bad intent
1071 return;
1072 }
1073
1074 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1075 op = PackageUpdatedTask.OP_UPDATE;
1076 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1077 if (!replacing) {
1078 op = PackageUpdatedTask.OP_REMOVE;
1079 }
1080 // else, we are replacing the package, so a PACKAGE_ADDED will be sent
1081 // later, we will update the package at this time
1082 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1083 if (!replacing) {
1084 op = PackageUpdatedTask.OP_ADD;
1085 } else {
1086 op = PackageUpdatedTask.OP_UPDATE;
1087 }
1088 }
1089
1090 if (op != PackageUpdatedTask.OP_NONE) {
1091 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
1092 }
1093
1094 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Winson Chung3ee4a472014-01-06 15:53:37 -08001095 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Joe Onoratocec58332010-10-07 14:37:40 -04001096 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Winson Chung3ee4a472014-01-06 15:53:37 -08001097 if (!replacing) {
1098 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
1099 if (mAppsCanBeOnRemoveableStorage) {
1100 // Only rebind if we support removable storage. It catches the case where
1101 // apps on the external sd card need to be reloaded
1102 startLoaderFromBackground();
1103 }
1104 } else {
1105 // If we are replacing then just update the packages in the list
1106 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1107 packages));
1108 }
Joe Onorato36115782010-06-17 13:28:48 -04001109 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Winson Chung3ee4a472014-01-06 15:53:37 -08001110 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1111 if (!replacing) {
1112 String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1113 enqueuePackageUpdated(new PackageUpdatedTask(
1114 PackageUpdatedTask.OP_UNAVAILABLE, packages));
1115 }
1116 // else, we are replacing the packages, so ignore this event and wait for
1117 // EXTERNAL_APPLICATIONS_AVAILABLE to update the packages at that time
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001118 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Reena Lee93f824a2011-09-23 17:20:28 -07001119 // If we have changed locale we need to clear out the labels in all apps/workspace.
1120 forceReload();
1121 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1122 // Check if configuration change was an mcc/mnc change which would affect app resources
1123 // and we would need to clear out the labels in all apps/workspace. Same handling as
1124 // above for ACTION_LOCALE_CHANGED
1125 Configuration currentConfig = context.getResources().getConfiguration();
Reena Lee99a73f32011-10-24 17:27:37 -07001126 if (mPreviousConfigMcc != currentConfig.mcc) {
Reena Lee93f824a2011-09-23 17:20:28 -07001127 Log.d(TAG, "Reload apps on config change. curr_mcc:"
Reena Lee99a73f32011-10-24 17:27:37 -07001128 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
Reena Lee93f824a2011-09-23 17:20:28 -07001129 forceReload();
1130 }
1131 // Update previousConfig
Reena Lee99a73f32011-10-24 17:27:37 -07001132 mPreviousConfigMcc = currentConfig.mcc;
Winson Chungcbf7c4d2011-08-23 11:58:54 -07001133 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1134 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
Michael Jurkaec9788e2011-08-29 11:24:45 -07001135 if (mCallbacks != null) {
1136 Callbacks callbacks = mCallbacks.get();
1137 if (callbacks != null) {
1138 callbacks.bindSearchablesChanged();
1139 }
Winson Chungcfdf7ee2011-08-25 11:38:34 -07001140 }
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001141 }
1142 }
1143
Reena Lee93f824a2011-09-23 17:20:28 -07001144 private void forceReload() {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001145 resetLoadedState(true, true);
1146
Reena Lee93f824a2011-09-23 17:20:28 -07001147 // Do this here because if the launcher activity is running it will be restarted.
1148 // If it's not running startLoaderFromBackground will merely tell it that it needs
1149 // to reload.
1150 startLoaderFromBackground();
1151 }
1152
Winson Chungf0c6ae02012-03-21 16:10:31 -07001153 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1154 synchronized (mLock) {
1155 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1156 // mWorkspaceLoaded to true later
1157 stopLoaderLocked();
1158 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1159 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1160 }
1161 }
1162
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001163 /**
1164 * When the launcher is in the background, it's possible for it to miss paired
1165 * configuration changes. So whenever we trigger the loader from the background
1166 * tell the launcher that it needs to re-run the loader when it comes back instead
1167 * of doing it now.
1168 */
1169 public void startLoaderFromBackground() {
1170 boolean runLoader = false;
1171 if (mCallbacks != null) {
1172 Callbacks callbacks = mCallbacks.get();
1173 if (callbacks != null) {
1174 // Only actually run the loader if they're not paused.
1175 if (!callbacks.setLoadOnResume()) {
1176 runLoader = true;
1177 }
1178 }
1179 }
1180 if (runLoader) {
Derek Prothro7aff3992013-12-10 14:00:37 -05001181 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
Joe Onorato790c2d92010-06-11 00:14:11 -07001182 }
Joe Onorato36115782010-06-17 13:28:48 -04001183 }
Joe Onoratof99f8c12009-10-31 17:27:36 -04001184
Reena Lee93f824a2011-09-23 17:20:28 -07001185 // If there is already a loader task running, tell it to stop.
1186 // returns true if isLaunching() was true on the old task
1187 private boolean stopLoaderLocked() {
1188 boolean isLaunching = false;
1189 LoaderTask oldTask = mLoaderTask;
1190 if (oldTask != null) {
1191 if (oldTask.isLaunching()) {
1192 isLaunching = true;
1193 }
1194 oldTask.stopLocked();
1195 }
1196 return isLaunching;
1197 }
1198
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001199 public void startLoader(boolean isLaunching, int synchronousBindPage) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001200 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1201 }
1202
1203 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
Joe Onorato36115782010-06-17 13:28:48 -04001204 synchronized (mLock) {
1205 if (DEBUG_LOADERS) {
1206 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1207 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001208
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001209 // Clear any deferred bind-runnables from the synchronized load process
1210 // We must do this before any loading/binding is scheduled below.
1211 mDeferredBindRunnables.clear();
1212
Joe Onorato36115782010-06-17 13:28:48 -04001213 // Don't bother to start the thread if we know it's not going to do anything
1214 if (mCallbacks != null && mCallbacks.get() != null) {
1215 // If there is already one running, tell it to stop.
Reena Lee93f824a2011-09-23 17:20:28 -07001216 // also, don't downgrade isLaunching if we're already running
1217 isLaunching = isLaunching || stopLoaderLocked();
Dan Sandlerd5024042014-01-09 15:01:33 -05001218 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
Derek Prothro7aff3992013-12-10 14:00:37 -05001219 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1220 && mAllAppsLoaded && mWorkspaceLoaded) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001221 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1222 } else {
1223 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1224 sWorker.post(mLoaderTask);
1225 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001226 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001227 }
1228 }
1229
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001230 void bindRemainingSynchronousPages() {
1231 // Post the remaining side pages to be loaded
1232 if (!mDeferredBindRunnables.isEmpty()) {
1233 for (final Runnable r : mDeferredBindRunnables) {
Winson Chung81b52252012-08-27 15:34:29 -07001234 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001235 }
1236 mDeferredBindRunnables.clear();
1237 }
1238 }
1239
Joe Onorato36115782010-06-17 13:28:48 -04001240 public void stopLoader() {
1241 synchronized (mLock) {
1242 if (mLoaderTask != null) {
1243 mLoaderTask.stopLocked();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001244 }
1245 }
Joe Onorato36115782010-06-17 13:28:48 -04001246 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001247
Winson Chung76828c82013-08-19 15:43:29 -07001248 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1249 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1250 final ContentResolver contentResolver = context.getContentResolver();
1251 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1252 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1253 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1254
1255 try {
1256 final int idIndex = sc.getColumnIndexOrThrow(
1257 LauncherSettings.WorkspaceScreens._ID);
1258 final int rankIndex = sc.getColumnIndexOrThrow(
1259 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1260 while (sc.moveToNext()) {
1261 try {
1262 long screenId = sc.getLong(idIndex);
1263 int rank = sc.getInt(rankIndex);
Winson Chung76828c82013-08-19 15:43:29 -07001264 orderedScreens.put(rank, screenId);
1265 } catch (Exception e) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001266 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true);
Winson Chung76828c82013-08-19 15:43:29 -07001267 }
1268 }
1269 } finally {
1270 sc.close();
1271 }
Winson Chunga90303b2013-11-15 13:05:06 -08001272
1273 // Log to disk
1274 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1275 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1276 for (Integer i : orderedScreens.keySet()) {
1277 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1278 }
1279 Launcher.addDumpLog(TAG, "11683562 - screens: " +
1280 TextUtils.join(", ", orderedScreensPairs), true);
Winson Chung76828c82013-08-19 15:43:29 -07001281 return orderedScreens;
1282 }
1283
Michael Jurkac57b7a82011-08-09 22:02:20 -07001284 public boolean isAllAppsLoaded() {
1285 return mAllAppsLoaded;
1286 }
1287
Winson Chung36a62fe2012-05-06 18:04:42 -07001288 boolean isLoadingWorkspace() {
1289 synchronized (mLock) {
1290 if (mLoaderTask != null) {
1291 return mLoaderTask.isLoadingWorkspace();
1292 }
1293 }
1294 return false;
1295 }
1296
Joe Onorato36115782010-06-17 13:28:48 -04001297 /**
1298 * Runnable for the thread that loads the contents of the launcher:
1299 * - workspace icons
1300 * - widgets
1301 * - all apps icons
1302 */
1303 private class LoaderTask implements Runnable {
1304 private Context mContext;
Joe Onorato36115782010-06-17 13:28:48 -04001305 private boolean mIsLaunching;
Winson Chung36a62fe2012-05-06 18:04:42 -07001306 private boolean mIsLoadingAndBindingWorkspace;
Joe Onorato36115782010-06-17 13:28:48 -04001307 private boolean mStopped;
1308 private boolean mLoadAndBindStepFinished;
Dan Sandlerd5024042014-01-09 15:01:33 -05001309 private int mFlags;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001310
Winson Chungc3eecff2011-07-11 17:44:15 -07001311 private HashMap<Object, CharSequence> mLabelCache;
Joe Onorato36115782010-06-17 13:28:48 -04001312
Dan Sandlerd5024042014-01-09 15:01:33 -05001313 LoaderTask(Context context, boolean isLaunching, int flags) {
Joe Onorato36115782010-06-17 13:28:48 -04001314 mContext = context;
1315 mIsLaunching = isLaunching;
Winson Chungc3eecff2011-07-11 17:44:15 -07001316 mLabelCache = new HashMap<Object, CharSequence>();
Dan Sandlerd5024042014-01-09 15:01:33 -05001317 mFlags = flags;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001318 }
1319
Joe Onorato36115782010-06-17 13:28:48 -04001320 boolean isLaunching() {
1321 return mIsLaunching;
1322 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001323
Winson Chung36a62fe2012-05-06 18:04:42 -07001324 boolean isLoadingWorkspace() {
1325 return mIsLoadingAndBindingWorkspace;
1326 }
1327
Winson Chungc763c4e2013-07-19 13:49:06 -07001328 /** Returns whether this is an upgrade path */
1329 private boolean loadAndBindWorkspace() {
Winson Chung36a62fe2012-05-06 18:04:42 -07001330 mIsLoadingAndBindingWorkspace = true;
1331
Joe Onorato36115782010-06-17 13:28:48 -04001332 // Load the workspace
Joe Onorato36115782010-06-17 13:28:48 -04001333 if (DEBUG_LOADERS) {
1334 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001335 }
Michael Jurka288a36b2011-07-12 16:53:48 -07001336
Winson Chungc763c4e2013-07-19 13:49:06 -07001337 boolean isUpgradePath = false;
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001338 if (!mWorkspaceLoaded) {
Winson Chungc763c4e2013-07-19 13:49:06 -07001339 isUpgradePath = loadWorkspace();
Reena Lee93f824a2011-09-23 17:20:28 -07001340 synchronized (LoaderTask.this) {
1341 if (mStopped) {
Winson Chungc763c4e2013-07-19 13:49:06 -07001342 return isUpgradePath;
Reena Lee93f824a2011-09-23 17:20:28 -07001343 }
1344 mWorkspaceLoaded = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001345 }
1346 }
1347
Joe Onorato36115782010-06-17 13:28:48 -04001348 // Bind the workspace
Winson Chungc763c4e2013-07-19 13:49:06 -07001349 bindWorkspace(-1, isUpgradePath);
1350 return isUpgradePath;
Joe Onorato36115782010-06-17 13:28:48 -04001351 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001352
Joe Onorato36115782010-06-17 13:28:48 -04001353 private void waitForIdle() {
1354 // Wait until the either we're stopped or the other threads are done.
1355 // This way we don't start loading all apps until the workspace has settled
1356 // down.
1357 synchronized (LoaderTask.this) {
1358 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onoratocc67f472010-06-08 10:54:30 -07001359
Joe Onorato36115782010-06-17 13:28:48 -04001360 mHandler.postIdle(new Runnable() {
1361 public void run() {
1362 synchronized (LoaderTask.this) {
1363 mLoadAndBindStepFinished = true;
1364 if (DEBUG_LOADERS) {
1365 Log.d(TAG, "done with previous binding step");
Daniel Sandler843e8602010-06-07 14:59:01 -04001366 }
Joe Onorato36115782010-06-17 13:28:48 -04001367 LoaderTask.this.notify();
Daniel Sandler843e8602010-06-07 14:59:01 -04001368 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001369 }
Joe Onorato36115782010-06-17 13:28:48 -04001370 });
1371
Michael Jurkac7700af2013-05-14 20:17:58 +02001372 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
Joe Onorato36115782010-06-17 13:28:48 -04001373 try {
Michael Jurkac7700af2013-05-14 20:17:58 +02001374 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1375 // wait no longer than 1sec at a time
1376 this.wait(1000);
Joe Onorato36115782010-06-17 13:28:48 -04001377 } catch (InterruptedException ex) {
1378 // Ignore
Daniel Sandler843e8602010-06-07 14:59:01 -04001379 }
1380 }
Joe Onorato36115782010-06-17 13:28:48 -04001381 if (DEBUG_LOADERS) {
1382 Log.d(TAG, "waited "
Winson Chungaafa03c2010-06-11 17:34:16 -07001383 + (SystemClock.uptimeMillis()-workspaceWaitTime)
Joe Onorato36115782010-06-17 13:28:48 -04001384 + "ms for previous step to finish binding");
1385 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001386 }
Joe Onorato36115782010-06-17 13:28:48 -04001387 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001388
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001389 void runBindSynchronousPage(int synchronousBindPage) {
Derek Prothro7aff3992013-12-10 14:00:37 -05001390 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001391 // Ensure that we have a valid page index to load synchronously
1392 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1393 "valid page index");
1394 }
1395 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1396 // Ensure that we don't try and bind a specified page when the pages have not been
1397 // loaded already (we should load everything asynchronously in that case)
1398 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1399 }
1400 synchronized (mLock) {
1401 if (mIsLoaderTaskRunning) {
1402 // Ensure that we are never running the background loading at this point since
1403 // we also touch the background collections
1404 throw new RuntimeException("Error! Background loading is already running");
1405 }
1406 }
1407
1408 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1409 // data structures, we can't allow any other thread to touch that data, but because
1410 // this call is synchronous, we can get away with not locking).
1411
Daniel Sandlercc8befa2013-06-11 14:45:48 -04001412 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
Adam Cohena13a2f22012-07-23 14:29:15 -07001413 // operations from the previous activity. We need to ensure that all queued operations
1414 // are executed before any synchronous binding work is done.
1415 mHandler.flush();
1416
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001417 // Divide the set of loaded items into those that we are binding synchronously, and
1418 // everything else that is to be bound normally (asynchronously).
Winson Chungc763c4e2013-07-19 13:49:06 -07001419 bindWorkspace(synchronousBindPage, false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001420 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1421 // arise from that.
1422 onlyBindAllApps();
1423 }
1424
Joe Onorato36115782010-06-17 13:28:48 -04001425 public void run() {
Winson Chungc763c4e2013-07-19 13:49:06 -07001426 boolean isUpgrade = false;
1427
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001428 synchronized (mLock) {
1429 mIsLoaderTaskRunning = true;
1430 }
Joe Onorato36115782010-06-17 13:28:48 -04001431 // Optimize for end-user experience: if the Launcher is up and // running with the
1432 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1433 // workspace first (default).
Joe Onorato36115782010-06-17 13:28:48 -04001434 keep_running: {
Daniel Sandler843e8602010-06-07 14:59:01 -04001435 // Elevate priority when Home launches for the first time to avoid
1436 // starving at boot time. Staring at a blank home is not cool.
1437 synchronized (mLock) {
Winson Chungaac01e12011-08-17 10:37:13 -07001438 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
1439 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
Daniel Sandler843e8602010-06-07 14:59:01 -04001440 android.os.Process.setThreadPriority(mIsLaunching
1441 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
1442 }
Winson Chung64359a52013-07-08 17:17:08 -07001443 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
Winson Chungc763c4e2013-07-19 13:49:06 -07001444 isUpgrade = loadAndBindWorkspace();
Daniel Sandler843e8602010-06-07 14:59:01 -04001445
Joe Onorato36115782010-06-17 13:28:48 -04001446 if (mStopped) {
1447 break keep_running;
1448 }
1449
1450 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1451 // settled down.
Daniel Sandler843e8602010-06-07 14:59:01 -04001452 synchronized (mLock) {
1453 if (mIsLaunching) {
Winson Chungaac01e12011-08-17 10:37:13 -07001454 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
Daniel Sandler843e8602010-06-07 14:59:01 -04001455 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1456 }
1457 }
Joe Onorato36115782010-06-17 13:28:48 -04001458 waitForIdle();
Daniel Sandler843e8602010-06-07 14:59:01 -04001459
1460 // second step
Winson Chung64359a52013-07-08 17:17:08 -07001461 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1462 loadAndBindAllApps();
Winson Chung7ed37742011-09-08 15:45:51 -07001463
1464 // Restore the default thread priority after we are done loading items
1465 synchronized (mLock) {
1466 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1467 }
Joe Onorato36115782010-06-17 13:28:48 -04001468 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001469
Winson Chungaac01e12011-08-17 10:37:13 -07001470 // Update the saved icons if necessary
1471 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
Winson Chung2abf94d2012-07-18 18:16:38 -07001472 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001473 for (Object key : sBgDbIconCache.keySet()) {
1474 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
1475 }
1476 sBgDbIconCache.clear();
Winson Chungaac01e12011-08-17 10:37:13 -07001477 }
Winson Chungaac01e12011-08-17 10:37:13 -07001478
Winson Chungc58497e2013-09-03 17:48:37 -07001479 if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
1480 // Ensure that all the applications that are in the system are
1481 // represented on the home screen.
Winson Chungc58497e2013-09-03 17:48:37 -07001482 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
Winson Chungc58497e2013-09-03 17:48:37 -07001483 verifyApplications();
1484 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001485 }
1486
Joe Onorato36115782010-06-17 13:28:48 -04001487 // Clear out this reference, otherwise we end up holding it until all of the
1488 // callback runnables are done.
1489 mContext = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001490
Joe Onorato36115782010-06-17 13:28:48 -04001491 synchronized (mLock) {
1492 // If we are still the last one to be scheduled, remove ourselves.
1493 if (mLoaderTask == this) {
1494 mLoaderTask = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001495 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001496 mIsLoaderTaskRunning = false;
Joe Onorato36115782010-06-17 13:28:48 -04001497 }
Joe Onorato36115782010-06-17 13:28:48 -04001498 }
1499
1500 public void stopLocked() {
1501 synchronized (LoaderTask.this) {
1502 mStopped = true;
1503 this.notify();
1504 }
1505 }
1506
1507 /**
1508 * Gets the callbacks object. If we've been stopped, or if the launcher object
1509 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1510 * object that was around when the deferred message was scheduled, and if there's
1511 * a new Callbacks object around then also return null. This will save us from
1512 * calling onto it with data that will be ignored.
1513 */
1514 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1515 synchronized (mLock) {
1516 if (mStopped) {
1517 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001518 }
Joe Onorato36115782010-06-17 13:28:48 -04001519
1520 if (mCallbacks == null) {
1521 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001522 }
Joe Onorato36115782010-06-17 13:28:48 -04001523
1524 final Callbacks callbacks = mCallbacks.get();
1525 if (callbacks != oldCallbacks) {
1526 return null;
1527 }
1528 if (callbacks == null) {
1529 Log.w(TAG, "no mCallbacks");
1530 return null;
1531 }
1532
1533 return callbacks;
1534 }
1535 }
1536
Winson Chungc763c4e2013-07-19 13:49:06 -07001537 private void verifyApplications() {
1538 final Context context = mApp.getContext();
1539
1540 // Cross reference all the applications in our apps list with items in the workspace
1541 ArrayList<ItemInfo> tmpInfos;
Michael Jurka695ff6b2013-08-05 12:06:48 +02001542 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
Winson Chungc763c4e2013-07-19 13:49:06 -07001543 synchronized (sBgLock) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02001544 for (AppInfo app : mBgAllAppsList.data) {
Winson Chungc763c4e2013-07-19 13:49:06 -07001545 tmpInfos = getItemInfoForComponentName(app.componentName);
1546 if (tmpInfos.isEmpty()) {
1547 // We are missing an application icon, so add this to the workspace
1548 added.add(app);
1549 // This is a rare event, so lets log it
1550 Log.e(TAG, "Missing Application on load: " + app);
1551 }
1552 }
1553 }
1554 if (!added.isEmpty()) {
1555 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Winson Chungfe9d96a2013-11-14 11:30:05 -08001556 addAndBindAddedApps(context, added, cb, new ArrayList<AppInfo>());
Winson Chungc763c4e2013-07-19 13:49:06 -07001557 }
1558 }
1559
Joe Onorato36115782010-06-17 13:28:48 -04001560 // check & update map of what's occupied; used to discard overlapping/invalid items
Winson Chunga0b7e862013-09-05 16:03:15 -07001561 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
Adam Cohenae4409d2013-11-26 10:34:59 -08001562 AtomicBoolean deleteOnInvalidPlacement) {
Winson Chung892c74d2013-08-22 16:15:50 -07001563 LauncherAppState app = LauncherAppState.getInstance();
1564 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Dan Sandler295ae182013-12-10 16:05:47 -05001565 final int countX = (int) grid.numColumns;
1566 final int countY = (int) grid.numRows;
Winson Chung892c74d2013-08-22 16:15:50 -07001567
Adam Cohendcd297f2013-06-18 13:13:40 -07001568 long containerIndex = item.screenId;
Winson Chungf30ad5f2011-08-08 10:55:42 -07001569 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001570 // Return early if we detect that an item is under the hotseat button
1571 if (mCallbacks == null ||
1572 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
Adam Cohenae4409d2013-11-26 10:34:59 -08001573 deleteOnInvalidPlacement.set(true);
Dan Sandler295ae182013-12-10 16:05:47 -05001574 Log.e(TAG, "Error loading shortcut into hotseat " + item
1575 + " into position (" + item.screenId + ":" + item.cellX + ","
1576 + item.cellY + ") occupied by all apps");
Winson Chunga0b7e862013-09-05 16:03:15 -07001577 return false;
1578 }
1579
Dan Sandler295ae182013-12-10 16:05:47 -05001580 final ItemInfo[][] hotseatItems =
1581 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1582
Adam Cohenae4409d2013-11-26 10:34:59 -08001583 if (item.screenId >= grid.numHotseatIcons) {
1584 Log.e(TAG, "Error loading shortcut " + item
1585 + " into hotseat position " + item.screenId
1586 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
1587 + ")");
1588 return false;
1589 }
1590
Dan Sandler295ae182013-12-10 16:05:47 -05001591 if (hotseatItems != null) {
1592 if (hotseatItems[(int) item.screenId][0] != null) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001593 Log.e(TAG, "Error loading shortcut into hotseat " + item
1594 + " into position (" + item.screenId + ":" + item.cellX + ","
1595 + item.cellY + ") occupied by "
1596 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1597 [(int) item.screenId][0]);
1598 return false;
Dan Sandler295ae182013-12-10 16:05:47 -05001599 } else {
1600 hotseatItems[(int) item.screenId][0] = item;
1601 return true;
Adam Cohendcd297f2013-06-18 13:13:40 -07001602 }
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001603 } else {
Adam Cohenae4409d2013-11-26 10:34:59 -08001604 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001605 items[(int) item.screenId][0] = item;
1606 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001607 return true;
1608 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001609 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1610 // Skip further checking if it is not the hotseat or workspace container
Daniel Sandler8802e962010-05-26 16:28:16 -04001611 return true;
1612 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001613
Adam Cohendcd297f2013-06-18 13:13:40 -07001614 if (!occupied.containsKey(item.screenId)) {
Winson Chung892c74d2013-08-22 16:15:50 -07001615 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001616 occupied.put(item.screenId, items);
1617 }
1618
Dan Sandler295ae182013-12-10 16:05:47 -05001619 final ItemInfo[][] screens = occupied.get(item.screenId);
Adam Cohenae4409d2013-11-26 10:34:59 -08001620 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1621 item.cellX < 0 || item.cellY < 0 ||
1622 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1623 Log.e(TAG, "Error loading shortcut " + item
1624 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1625 + item.cellX + "," + item.cellY
1626 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1627 return false;
1628 }
1629
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001630 // Check if any workspace icons overlap with each other
Joe Onorato36115782010-06-17 13:28:48 -04001631 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1632 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001633 if (screens[x][y] != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001634 Log.e(TAG, "Error loading shortcut " + item
Adam Cohendcd297f2013-06-18 13:13:40 -07001635 + " into cell (" + containerIndex + "-" + item.screenId + ":"
Joe Onorato36115782010-06-17 13:28:48 -04001636 + x + "," + y
Winson Chungaafa03c2010-06-11 17:34:16 -07001637 + ") occupied by "
Adam Cohendcd297f2013-06-18 13:13:40 -07001638 + screens[x][y]);
Joe Onorato36115782010-06-17 13:28:48 -04001639 return false;
1640 }
1641 }
1642 }
1643 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1644 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001645 screens[x][y] = item;
Joe Onorato36115782010-06-17 13:28:48 -04001646 }
1647 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001648
Joe Onorato36115782010-06-17 13:28:48 -04001649 return true;
1650 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001651
Winson Chungba9c37f2013-08-30 14:11:37 -07001652 /** Clears all the sBg data structures */
1653 private void clearSBgDataStructures() {
1654 synchronized (sBgLock) {
1655 sBgWorkspaceItems.clear();
1656 sBgAppWidgets.clear();
1657 sBgFolders.clear();
1658 sBgItemsIdMap.clear();
1659 sBgDbIconCache.clear();
1660 sBgWorkspaceScreens.clear();
1661 }
1662 }
1663
Dan Sandlerd5024042014-01-09 15:01:33 -05001664 /** Returns whether this is an upgrade path */
Winson Chungc763c4e2013-07-19 13:49:06 -07001665 private boolean loadWorkspace() {
Winson Chung9f9f00b2013-11-15 13:27:00 -08001666 // Log to disk
1667 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1668
Joe Onorato36115782010-06-17 13:28:48 -04001669 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001670
Joe Onorato36115782010-06-17 13:28:48 -04001671 final Context context = mContext;
1672 final ContentResolver contentResolver = context.getContentResolver();
1673 final PackageManager manager = context.getPackageManager();
1674 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1675 final boolean isSafeMode = manager.isSafeMode();
Joe Onorato3c2f7e12009-10-31 19:17:31 -04001676
Winson Chung892c74d2013-08-22 16:15:50 -07001677 LauncherAppState app = LauncherAppState.getInstance();
1678 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1679 int countX = (int) grid.numColumns;
1680 int countY = (int) grid.numRows;
1681
Dan Sandlerd5024042014-01-09 15:01:33 -05001682 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1683 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1684 LauncherAppState.getLauncherProvider().deleteDatabase();
1685 }
1686
1687 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1688 // append the user's Launcher2 shortcuts
1689 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1690 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1691 } else {
1692 // Make sure the default workspace is loaded
1693 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
1694 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
1695 }
Adam Cohene25af792013-06-06 23:08:25 -07001696
Winson Chungc763c4e2013-07-19 13:49:06 -07001697 // Check if we need to do any upgrade-path logic
Dan Sandlerf0b8dac2013-11-19 12:21:25 -05001698 // (Includes having just imported default favorites)
Michael Jurka414300a2013-08-27 15:42:35 +02001699 boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb();
Dan Sandlerf0b8dac2013-11-19 12:21:25 -05001700
Winson Chung9f9f00b2013-11-15 13:27:00 -08001701 // Log to disk
1702 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
Michael Jurkab85f8a42012-04-25 15:48:32 -07001703
Winson Chung2abf94d2012-07-18 18:16:38 -07001704 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001705 clearSBgDataStructures();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001706
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001707 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
Winson Chungc763c4e2013-07-19 13:49:06 -07001708 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
Chris Wrene523e702013-10-09 10:36:55 -04001709 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
Adam Cohene25af792013-06-06 23:08:25 -07001710 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
Daniel Sandler8802e962010-05-26 16:28:16 -04001711
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001712 // +1 for the hotseat (it can be larger than the workspace)
1713 // Load workspace in reverse order to ensure that latest items are loaded first (and
1714 // before any earlier duplicates)
Adam Cohendcd297f2013-06-18 13:13:40 -07001715 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001716
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001717 try {
1718 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1719 final int intentIndex = c.getColumnIndexOrThrow
1720 (LauncherSettings.Favorites.INTENT);
1721 final int titleIndex = c.getColumnIndexOrThrow
1722 (LauncherSettings.Favorites.TITLE);
1723 final int iconTypeIndex = c.getColumnIndexOrThrow(
1724 LauncherSettings.Favorites.ICON_TYPE);
1725 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1726 final int iconPackageIndex = c.getColumnIndexOrThrow(
1727 LauncherSettings.Favorites.ICON_PACKAGE);
1728 final int iconResourceIndex = c.getColumnIndexOrThrow(
1729 LauncherSettings.Favorites.ICON_RESOURCE);
1730 final int containerIndex = c.getColumnIndexOrThrow(
1731 LauncherSettings.Favorites.CONTAINER);
1732 final int itemTypeIndex = c.getColumnIndexOrThrow(
1733 LauncherSettings.Favorites.ITEM_TYPE);
1734 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1735 LauncherSettings.Favorites.APPWIDGET_ID);
Chris Wrenc3919c02013-09-18 09:48:33 -04001736 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1737 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001738 final int screenIndex = c.getColumnIndexOrThrow(
1739 LauncherSettings.Favorites.SCREEN);
1740 final int cellXIndex = c.getColumnIndexOrThrow
1741 (LauncherSettings.Favorites.CELLX);
1742 final int cellYIndex = c.getColumnIndexOrThrow
1743 (LauncherSettings.Favorites.CELLY);
1744 final int spanXIndex = c.getColumnIndexOrThrow
1745 (LauncherSettings.Favorites.SPANX);
1746 final int spanYIndex = c.getColumnIndexOrThrow(
1747 LauncherSettings.Favorites.SPANY);
1748 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1749 //final int displayModeIndex = c.getColumnIndexOrThrow(
1750 // LauncherSettings.Favorites.DISPLAY_MODE);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001751
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001752 ShortcutInfo info;
1753 String intentDescription;
1754 LauncherAppWidgetInfo appWidgetInfo;
1755 int container;
1756 long id;
1757 Intent intent;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001758
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001759 while (!mStopped && c.moveToNext()) {
Adam Cohenae4409d2013-11-26 10:34:59 -08001760 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001761 try {
1762 int itemType = c.getInt(itemTypeIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001763
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001764 switch (itemType) {
1765 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1766 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Winson Chungee055712013-07-30 14:46:24 -07001767 id = c.getLong(idIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001768 intentDescription = c.getString(intentIndex);
1769 try {
1770 intent = Intent.parseUri(intentDescription, 0);
Winson Chungee055712013-07-30 14:46:24 -07001771 ComponentName cn = intent.getComponent();
Winson Chung68fd3c32013-08-30 16:38:00 -07001772 if (cn != null && !isValidPackageComponent(manager, cn)) {
Winson Chungee055712013-07-30 14:46:24 -07001773 if (!mAppsCanBeOnRemoveableStorage) {
Winson Chung1323b482013-08-05 12:41:55 -07001774 // Log the invalid package, and remove it from the db
Winson Chunga0b7e862013-09-05 16:03:15 -07001775 Launcher.addDumpLog(TAG, "Invalid package removed: " + cn, true);
1776 itemsToRemove.add(id);
Winson Chungee055712013-07-30 14:46:24 -07001777 } else {
Winson Chung1323b482013-08-05 12:41:55 -07001778 // If apps can be on external storage, then we just
1779 // leave them for the user to remove (maybe add
1780 // visual treatment to it)
Winson Chung933bae62013-08-29 11:42:30 -07001781 Launcher.addDumpLog(TAG, "Invalid package found: " + cn, true);
Winson Chungee055712013-07-30 14:46:24 -07001782 }
1783 continue;
1784 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001785 } catch (URISyntaxException e) {
Winson Chung933bae62013-08-29 11:42:30 -07001786 Launcher.addDumpLog(TAG, "Invalid uri: " + intentDescription, true);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001787 continue;
1788 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001789
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001790 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
1791 info = getShortcutInfo(manager, intent, context, c, iconIndex,
1792 titleIndex, mLabelCache);
1793 } else {
1794 info = getShortcutInfo(c, context, iconTypeIndex,
1795 iconPackageIndex, iconResourceIndex, iconIndex,
1796 titleIndex);
Michael Jurka96879562012-03-22 05:54:33 -07001797
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001798 // App shortcuts that used to be automatically added to Launcher
1799 // didn't always have the correct intent flags set, so do that
1800 // here
1801 if (intent.getAction() != null &&
Michael Jurka9ad00562012-05-14 12:24:22 -07001802 intent.getCategories() != null &&
1803 intent.getAction().equals(Intent.ACTION_MAIN) &&
Michael Jurka96879562012-03-22 05:54:33 -07001804 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001805 intent.addFlags(
1806 Intent.FLAG_ACTIVITY_NEW_TASK |
1807 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1808 }
Michael Jurka96879562012-03-22 05:54:33 -07001809 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001810
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001811 if (info != null) {
Winson Chungee055712013-07-30 14:46:24 -07001812 info.id = id;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001813 info.intent = intent;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001814 container = c.getInt(containerIndex);
1815 info.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07001816 info.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001817 info.cellX = c.getInt(cellXIndex);
1818 info.cellY = c.getInt(cellYIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07001819 info.spanX = 1;
1820 info.spanY = 1;
Adam Cohenae4409d2013-11-26 10:34:59 -08001821
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001822 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08001823 deleteOnInvalidPlacement.set(false);
1824 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
1825 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001826 itemsToRemove.add(id);
1827 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001828 break;
1829 }
1830
1831 switch (container) {
1832 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
1833 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
1834 sBgWorkspaceItems.add(info);
1835 break;
1836 default:
1837 // Item is in a user folder
1838 FolderInfo folderInfo =
1839 findOrMakeFolder(sBgFolders, container);
1840 folderInfo.add(info);
1841 break;
1842 }
1843 sBgItemsIdMap.put(info.id, info);
1844
1845 // now that we've loaded everthing re-save it with the
1846 // icon in case it disappears somehow.
1847 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
Winson Chung1323b482013-08-05 12:41:55 -07001848 } else {
1849 throw new RuntimeException("Unexpected null ShortcutInfo");
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001850 }
1851 break;
1852
1853 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1854 id = c.getLong(idIndex);
1855 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
1856
1857 folderInfo.title = c.getString(titleIndex);
1858 folderInfo.id = id;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001859 container = c.getInt(containerIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001860 folderInfo.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07001861 folderInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001862 folderInfo.cellX = c.getInt(cellXIndex);
1863 folderInfo.cellY = c.getInt(cellYIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07001864 folderInfo.spanX = 1;
1865 folderInfo.spanY = 1;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001866
Daniel Sandler8802e962010-05-26 16:28:16 -04001867 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08001868 deleteOnInvalidPlacement.set(false);
Winson Chunga0b7e862013-09-05 16:03:15 -07001869 if (!checkItemPlacement(occupied, folderInfo,
Adam Cohenae4409d2013-11-26 10:34:59 -08001870 deleteOnInvalidPlacement)) {
1871 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001872 itemsToRemove.add(id);
1873 }
Daniel Sandler8802e962010-05-26 16:28:16 -04001874 break;
1875 }
Winson Chung5f8afe62013-08-12 16:19:28 -07001876
Joe Onorato9c1289c2009-08-17 11:03:03 -04001877 switch (container) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001878 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
1879 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
1880 sBgWorkspaceItems.add(folderInfo);
1881 break;
Joe Onorato36115782010-06-17 13:28:48 -04001882 }
Joe Onorato17a89222011-02-08 17:26:11 -08001883
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001884 sBgItemsIdMap.put(folderInfo.id, folderInfo);
1885 sBgFolders.put(folderInfo.id, folderInfo);
1886 break;
1887
1888 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1889 // Read all Launcher-specific widget details
1890 int appWidgetId = c.getInt(appWidgetIdIndex);
Chris Wrenc3919c02013-09-18 09:48:33 -04001891 String savedProvider = c.getString(appWidgetProviderIndex);
1892
Joe Onorato36115782010-06-17 13:28:48 -04001893 id = c.getLong(idIndex);
Joe Onorato36115782010-06-17 13:28:48 -04001894
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001895 final AppWidgetProviderInfo provider =
1896 widgets.getAppWidgetInfo(appWidgetId);
Joe Onorato36115782010-06-17 13:28:48 -04001897
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001898 if (!isSafeMode && (provider == null || provider.provider == null ||
1899 provider.provider.getPackageName() == null)) {
1900 String log = "Deleting widget that isn't installed anymore: id="
1901 + id + " appWidgetId=" + appWidgetId;
1902 Log.e(TAG, log);
Adam Cohen4caf2982013-08-20 18:54:31 -07001903 Launcher.addDumpLog(TAG, log, false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001904 itemsToRemove.add(id);
1905 } else {
1906 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
1907 provider.provider);
1908 appWidgetInfo.id = id;
Adam Cohendcd297f2013-06-18 13:13:40 -07001909 appWidgetInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001910 appWidgetInfo.cellX = c.getInt(cellXIndex);
1911 appWidgetInfo.cellY = c.getInt(cellYIndex);
1912 appWidgetInfo.spanX = c.getInt(spanXIndex);
1913 appWidgetInfo.spanY = c.getInt(spanYIndex);
1914 int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
1915 appWidgetInfo.minSpanX = minSpan[0];
1916 appWidgetInfo.minSpanY = minSpan[1];
Joe Onorato36115782010-06-17 13:28:48 -04001917
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001918 container = c.getInt(containerIndex);
1919 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1920 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1921 Log.e(TAG, "Widget found where container != " +
1922 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
1923 continue;
1924 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001925
Adam Cohene25af792013-06-06 23:08:25 -07001926 appWidgetInfo.container = c.getInt(containerIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001927 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08001928 deleteOnInvalidPlacement.set(false);
Winson Chunga0b7e862013-09-05 16:03:15 -07001929 if (!checkItemPlacement(occupied, appWidgetInfo,
Adam Cohenae4409d2013-11-26 10:34:59 -08001930 deleteOnInvalidPlacement)) {
1931 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001932 itemsToRemove.add(id);
1933 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001934 break;
1935 }
Chris Wrenc3919c02013-09-18 09:48:33 -04001936 String providerName = provider.provider.flattenToString();
1937 if (!providerName.equals(savedProvider)) {
1938 ContentValues values = new ContentValues();
1939 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
1940 providerName);
1941 String where = BaseColumns._ID + "= ?";
1942 String[] args = {Integer.toString(c.getInt(idIndex))};
1943 contentResolver.update(contentUri, values, where, args);
1944 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001945 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
1946 sBgAppWidgets.add(appWidgetInfo);
1947 }
Joe Onorato36115782010-06-17 13:28:48 -04001948 break;
1949 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001950 } catch (Exception e) {
Dan Sandler295ae182013-12-10 16:05:47 -05001951 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
Romain Guy5c16f3e2010-01-12 17:24:58 -08001952 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001953 }
1954 } finally {
Daniel Sandler47b50312013-07-25 13:16:14 -04001955 if (c != null) {
1956 c.close();
1957 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001958 }
1959
Winson Chungba9c37f2013-08-30 14:11:37 -07001960 // Break early if we've stopped loading
1961 if (mStopped) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001962 clearSBgDataStructures();
1963 return false;
1964 }
1965
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001966 if (itemsToRemove.size() > 0) {
1967 ContentProviderClient client = contentResolver.acquireContentProviderClient(
Adam Cohen4caf2982013-08-20 18:54:31 -07001968 LauncherSettings.Favorites.CONTENT_URI);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001969 // Remove dead items
1970 for (long id : itemsToRemove) {
1971 if (DEBUG_LOADERS) {
1972 Log.d(TAG, "Removed id = " + id);
1973 }
1974 // Don't notify content observers
1975 try {
1976 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
1977 null, null);
1978 } catch (RemoteException e) {
1979 Log.w(TAG, "Could not remove id = " + id);
1980 }
Romain Guy5c16f3e2010-01-12 17:24:58 -08001981 }
1982 }
1983
Winson Chungc763c4e2013-07-19 13:49:06 -07001984 if (loadedOldDb) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001985 long maxScreenId = 0;
1986 // If we're importing we use the old screen order.
1987 for (ItemInfo item: sBgItemsIdMap.values()) {
1988 long screenId = item.screenId;
1989 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1990 !sBgWorkspaceScreens.contains(screenId)) {
1991 sBgWorkspaceScreens.add(screenId);
1992 if (screenId > maxScreenId) {
1993 maxScreenId = screenId;
1994 }
1995 }
1996 }
1997 Collections.sort(sBgWorkspaceScreens);
Winson Chung9f9f00b2013-11-15 13:27:00 -08001998 // Log to disk
1999 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2000 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2001 TextUtils.join(", ", sBgWorkspaceScreens), true);
Winson Chung9e6a0a22013-08-27 11:58:12 -07002002
Michael Jurka414300a2013-08-27 15:42:35 +02002003 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
Adam Cohendcd297f2013-06-18 13:13:40 -07002004 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
Winson Chungc763c4e2013-07-19 13:49:06 -07002005
2006 // Update the max item id after we load an old db
2007 long maxItemId = 0;
2008 // If we're importing we use the old screen order.
2009 for (ItemInfo item: sBgItemsIdMap.values()) {
2010 maxItemId = Math.max(maxItemId, item.id);
2011 }
Michael Jurka414300a2013-08-27 15:42:35 +02002012 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
Adam Cohendcd297f2013-06-18 13:13:40 -07002013 } else {
Winson Chung76828c82013-08-19 15:43:29 -07002014 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2015 for (Integer i : orderedScreens.keySet()) {
2016 sBgWorkspaceScreens.add(orderedScreens.get(i));
Adam Cohendcd297f2013-06-18 13:13:40 -07002017 }
Winson Chung9f9f00b2013-11-15 13:27:00 -08002018 // Log to disk
2019 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2020 TextUtils.join(", ", sBgWorkspaceScreens), true);
Adam Cohendcd297f2013-06-18 13:13:40 -07002021
2022 // Remove any empty screens
Winson Chung933bae62013-08-29 11:42:30 -07002023 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002024 for (ItemInfo item: sBgItemsIdMap.values()) {
2025 long screenId = item.screenId;
Adam Cohendcd297f2013-06-18 13:13:40 -07002026 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2027 unusedScreens.contains(screenId)) {
2028 unusedScreens.remove(screenId);
2029 }
2030 }
2031
2032 // If there are any empty screens remove them, and update.
2033 if (unusedScreens.size() != 0) {
Winson Chung9f9f00b2013-11-15 13:27:00 -08002034 // Log to disk
2035 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
2036 TextUtils.join(", ", unusedScreens), true);
2037
Winson Chung933bae62013-08-29 11:42:30 -07002038 sBgWorkspaceScreens.removeAll(unusedScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002039 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2040 }
2041 }
2042
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002043 if (DEBUG_LOADERS) {
2044 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2045 Log.d(TAG, "workspace layout: ");
Adam Cohendcd297f2013-06-18 13:13:40 -07002046 int nScreens = occupied.size();
Winson Chung892c74d2013-08-22 16:15:50 -07002047 for (int y = 0; y < countY; y++) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002048 String line = "";
Adam Cohendcd297f2013-06-18 13:13:40 -07002049
Daniel Sandler566da102013-06-25 23:43:45 -04002050 Iterator<Long> iter = occupied.keySet().iterator();
Winson Chungc9168342013-06-26 14:54:55 -07002051 while (iter.hasNext()) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002052 long screenId = iter.next();
Winson Chungc9168342013-06-26 14:54:55 -07002053 if (screenId > 0) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002054 line += " | ";
2055 }
Winson Chung892c74d2013-08-22 16:15:50 -07002056 for (int x = 0; x < countX; x++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002057 line += ((occupied.get(screenId)[x][y] != null) ? "#" : ".");
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002058 }
Joe Onorato36115782010-06-17 13:28:48 -04002059 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002060 Log.d(TAG, "[ " + line + " ]");
Joe Onorato36115782010-06-17 13:28:48 -04002061 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002062 }
Joe Onorato36115782010-06-17 13:28:48 -04002063 }
Winson Chungc763c4e2013-07-19 13:49:06 -07002064 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -07002065 }
2066
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002067 /** Filters the set of items who are directly or indirectly (via another container) on the
2068 * specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002069 private void filterCurrentWorkspaceItems(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002070 ArrayList<ItemInfo> allWorkspaceItems,
2071 ArrayList<ItemInfo> currentScreenItems,
2072 ArrayList<ItemInfo> otherScreenItems) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002073 // Purge any null ItemInfos
2074 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2075 while (iter.hasNext()) {
2076 ItemInfo i = iter.next();
2077 if (i == null) {
2078 iter.remove();
2079 }
2080 }
2081
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002082 // Order the set of items by their containers first, this allows use to walk through the
2083 // list sequentially, build up a list of containers that are in the specified screen,
2084 // as well as all items in those containers.
2085 Set<Long> itemsOnScreen = new HashSet<Long>();
2086 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2087 @Override
2088 public int compare(ItemInfo lhs, ItemInfo rhs) {
2089 return (int) (lhs.container - rhs.container);
2090 }
2091 });
2092 for (ItemInfo info : allWorkspaceItems) {
2093 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Winson Chung9b9fb962013-11-15 15:39:34 -08002094 if (info.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002095 currentScreenItems.add(info);
2096 itemsOnScreen.add(info.id);
2097 } else {
2098 otherScreenItems.add(info);
2099 }
2100 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2101 currentScreenItems.add(info);
2102 itemsOnScreen.add(info.id);
2103 } else {
2104 if (itemsOnScreen.contains(info.container)) {
2105 currentScreenItems.add(info);
2106 itemsOnScreen.add(info.id);
2107 } else {
2108 otherScreenItems.add(info);
2109 }
2110 }
2111 }
2112 }
2113
2114 /** Filters the set of widgets which are on the specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002115 private void filterCurrentAppWidgets(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002116 ArrayList<LauncherAppWidgetInfo> appWidgets,
2117 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2118 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002119
2120 for (LauncherAppWidgetInfo widget : appWidgets) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002121 if (widget == null) continue;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002122 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
Winson Chung9b9fb962013-11-15 15:39:34 -08002123 widget.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002124 currentScreenWidgets.add(widget);
2125 } else {
2126 otherScreenWidgets.add(widget);
2127 }
2128 }
2129 }
2130
2131 /** Filters the set of folders which are on the specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002132 private void filterCurrentFolders(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002133 HashMap<Long, ItemInfo> itemsIdMap,
2134 HashMap<Long, FolderInfo> folders,
2135 HashMap<Long, FolderInfo> currentScreenFolders,
2136 HashMap<Long, FolderInfo> otherScreenFolders) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002137
2138 for (long id : folders.keySet()) {
2139 ItemInfo info = itemsIdMap.get(id);
2140 FolderInfo folder = folders.get(id);
Winson Chung2abf94d2012-07-18 18:16:38 -07002141 if (info == null || folder == null) continue;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002142 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
Winson Chung9b9fb962013-11-15 15:39:34 -08002143 info.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002144 currentScreenFolders.put(id, folder);
2145 } else {
2146 otherScreenFolders.put(id, folder);
2147 }
2148 }
2149 }
2150
2151 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2152 * right) */
2153 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
Winson Chung892c74d2013-08-22 16:15:50 -07002154 final LauncherAppState app = LauncherAppState.getInstance();
2155 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002156 // XXX: review this
2157 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
Winson Chungdb8a8942012-04-03 14:08:41 -07002158 @Override
2159 public int compare(ItemInfo lhs, ItemInfo rhs) {
Winson Chung892c74d2013-08-22 16:15:50 -07002160 int cellCountX = (int) grid.numColumns;
2161 int cellCountY = (int) grid.numRows;
Winson Chungdb8a8942012-04-03 14:08:41 -07002162 int screenOffset = cellCountX * cellCountY;
2163 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -07002164 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
Winson Chungdb8a8942012-04-03 14:08:41 -07002165 lhs.cellY * cellCountX + lhs.cellX);
Adam Cohendcd297f2013-06-18 13:13:40 -07002166 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
Winson Chungdb8a8942012-04-03 14:08:41 -07002167 rhs.cellY * cellCountX + rhs.cellX);
2168 return (int) (lr - rr);
2169 }
2170 });
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002171 }
Winson Chungdb8a8942012-04-03 14:08:41 -07002172
Adam Cohendcd297f2013-06-18 13:13:40 -07002173 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2174 final ArrayList<Long> orderedScreens) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002175 final Runnable r = new Runnable() {
2176 @Override
2177 public void run() {
2178 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2179 if (callbacks != null) {
2180 callbacks.bindScreens(orderedScreens);
2181 }
2182 }
2183 };
2184 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2185 }
2186
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002187 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2188 final ArrayList<ItemInfo> workspaceItems,
2189 final ArrayList<LauncherAppWidgetInfo> appWidgets,
2190 final HashMap<Long, FolderInfo> folders,
2191 ArrayList<Runnable> deferredBindRunnables) {
Winson Chung603bcb92011-09-02 11:45:39 -07002192
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002193 final boolean postOnMainThread = (deferredBindRunnables != null);
2194
2195 // Bind the workspace items
Winson Chungdb8a8942012-04-03 14:08:41 -07002196 int N = workspaceItems.size();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002197 for (int i = 0; i < N; i += ITEMS_CHUNK) {
Joe Onorato36115782010-06-17 13:28:48 -04002198 final int start = i;
2199 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002200 final Runnable r = new Runnable() {
2201 @Override
Joe Onorato9c1289c2009-08-17 11:03:03 -04002202 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08002203 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002204 if (callbacks != null) {
Winson Chung64359a52013-07-08 17:17:08 -07002205 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2206 false);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002207 }
2208 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002209 };
2210 if (postOnMainThread) {
2211 deferredBindRunnables.add(r);
2212 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002213 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002214 }
Joe Onorato36115782010-06-17 13:28:48 -04002215 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002216
2217 // Bind the folders
2218 if (!folders.isEmpty()) {
2219 final Runnable r = new Runnable() {
2220 public void run() {
2221 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2222 if (callbacks != null) {
2223 callbacks.bindFolders(folders);
2224 }
2225 }
2226 };
2227 if (postOnMainThread) {
2228 deferredBindRunnables.add(r);
2229 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002230 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002231 }
2232 }
2233
2234 // Bind the widgets, one at a time
2235 N = appWidgets.size();
2236 for (int i = 0; i < N; i++) {
2237 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2238 final Runnable r = new Runnable() {
2239 public void run() {
2240 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2241 if (callbacks != null) {
2242 callbacks.bindAppWidget(widget);
2243 }
2244 }
2245 };
2246 if (postOnMainThread) {
2247 deferredBindRunnables.add(r);
2248 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002249 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002250 }
2251 }
2252 }
2253
2254 /**
2255 * Binds all loaded data to actual views on the main thread.
2256 */
Winson Chungc763c4e2013-07-19 13:49:06 -07002257 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002258 final long t = SystemClock.uptimeMillis();
2259 Runnable r;
2260
2261 // Don't use these two variables in any of the callback runnables.
2262 // Otherwise we hold a reference to them.
2263 final Callbacks oldCallbacks = mCallbacks.get();
2264 if (oldCallbacks == null) {
2265 // This launcher has exited and nobody bothered to tell us. Just bail.
2266 Log.w(TAG, "LoaderTask running with no launcher");
2267 return;
2268 }
2269
Winson Chung9b9fb962013-11-15 15:39:34 -08002270 // Save a copy of all the bg-thread collections
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002271 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2272 ArrayList<LauncherAppWidgetInfo> appWidgets =
2273 new ArrayList<LauncherAppWidgetInfo>();
2274 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2275 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
Adam Cohendcd297f2013-06-18 13:13:40 -07002276 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
Winson Chung2abf94d2012-07-18 18:16:38 -07002277 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002278 workspaceItems.addAll(sBgWorkspaceItems);
2279 appWidgets.addAll(sBgAppWidgets);
2280 folders.putAll(sBgFolders);
2281 itemsIdMap.putAll(sBgItemsIdMap);
Adam Cohendcd297f2013-06-18 13:13:40 -07002282 orderedScreenIds.addAll(sBgWorkspaceScreens);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002283 }
2284
Derek Prothro7aff3992013-12-10 14:00:37 -05002285 final boolean isLoadingSynchronously =
2286 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
Adam Cohend8dbb462013-11-27 11:55:48 -08002287 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
Winson Chung9b9fb962013-11-15 15:39:34 -08002288 oldCallbacks.getCurrentWorkspaceScreen();
Adam Cohend8dbb462013-11-27 11:55:48 -08002289 if (currScreen >= orderedScreenIds.size()) {
2290 // There may be no workspace screens (just hotseat items and an empty page).
Derek Prothro7aff3992013-12-10 14:00:37 -05002291 currScreen = PagedView.INVALID_RESTORE_PAGE;
Winson Chung9b9fb962013-11-15 15:39:34 -08002292 }
Adam Cohend8dbb462013-11-27 11:55:48 -08002293 final int currentScreen = currScreen;
Derek Prothro7aff3992013-12-10 14:00:37 -05002294 final long currentScreenId = currentScreen < 0
2295 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
Winson Chung9b9fb962013-11-15 15:39:34 -08002296
2297 // Load all the items that are on the current page first (and in the process, unbind
2298 // all the existing workspace items before we call startBinding() below.
2299 unbindWorkspaceItemsOnMainThread();
2300
2301 // Separate the items that are on the current screen, and all the other remaining items
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002302 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2303 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2304 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
2305 new ArrayList<LauncherAppWidgetInfo>();
2306 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
2307 new ArrayList<LauncherAppWidgetInfo>();
2308 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2309 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2310
Winson Chung9b9fb962013-11-15 15:39:34 -08002311 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002312 otherWorkspaceItems);
Winson Chung9b9fb962013-11-15 15:39:34 -08002313 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002314 otherAppWidgets);
Winson Chung9b9fb962013-11-15 15:39:34 -08002315 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002316 otherFolders);
2317 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2318 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2319
2320 // Tell the workspace that we're about to start binding items
2321 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002322 public void run() {
2323 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2324 if (callbacks != null) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002325 callbacks.startBinding();
Joe Onorato36115782010-06-17 13:28:48 -04002326 }
2327 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002328 };
Winson Chung81b52252012-08-27 15:34:29 -07002329 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002330
Adam Cohendcd297f2013-06-18 13:13:40 -07002331 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2332
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002333 // Load items on the current page
2334 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
2335 currentFolders, null);
Adam Cohen1462de32012-07-24 22:34:36 -07002336 if (isLoadingSynchronously) {
2337 r = new Runnable() {
2338 public void run() {
2339 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Derek Prothro7aff3992013-12-10 14:00:37 -05002340 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
Adam Cohen1462de32012-07-24 22:34:36 -07002341 callbacks.onPageBoundSynchronously(currentScreen);
2342 }
2343 }
2344 };
Winson Chung81b52252012-08-27 15:34:29 -07002345 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Adam Cohen1462de32012-07-24 22:34:36 -07002346 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002347
Winson Chung4a2afa32012-07-19 14:53:05 -07002348 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2349 // work until after the first render)
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002350 mDeferredBindRunnables.clear();
2351 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
Winson Chung4a2afa32012-07-19 14:53:05 -07002352 (isLoadingSynchronously ? mDeferredBindRunnables : null));
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002353
2354 // Tell the workspace that we're done binding items
2355 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002356 public void run() {
2357 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2358 if (callbacks != null) {
Winson Chungc763c4e2013-07-19 13:49:06 -07002359 callbacks.finishBindingItems(isUpgradePath);
Joe Onorato36115782010-06-17 13:28:48 -04002360 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002361
Winson Chung98e030b2012-05-07 16:01:11 -07002362 // If we're profiling, ensure this is the last thing in the queue.
Joe Onorato36115782010-06-17 13:28:48 -04002363 if (DEBUG_LOADERS) {
2364 Log.d(TAG, "bound workspace in "
2365 + (SystemClock.uptimeMillis()-t) + "ms");
2366 }
Winson Chung36a62fe2012-05-06 18:04:42 -07002367
2368 mIsLoadingAndBindingWorkspace = false;
Joe Onorato36115782010-06-17 13:28:48 -04002369 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002370 };
Winson Chung4a2afa32012-07-19 14:53:05 -07002371 if (isLoadingSynchronously) {
2372 mDeferredBindRunnables.add(r);
2373 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002374 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chung4a2afa32012-07-19 14:53:05 -07002375 }
Joe Onorato36115782010-06-17 13:28:48 -04002376 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002377
Joe Onorato36115782010-06-17 13:28:48 -04002378 private void loadAndBindAllApps() {
2379 if (DEBUG_LOADERS) {
2380 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2381 }
2382 if (!mAllAppsLoaded) {
Winson Chung64359a52013-07-08 17:17:08 -07002383 loadAllApps();
Reena Lee93f824a2011-09-23 17:20:28 -07002384 synchronized (LoaderTask.this) {
2385 if (mStopped) {
2386 return;
2387 }
2388 mAllAppsLoaded = true;
Joe Onoratocc67f472010-06-08 10:54:30 -07002389 }
Joe Onorato36115782010-06-17 13:28:48 -04002390 } else {
2391 onlyBindAllApps();
2392 }
2393 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002394
Joe Onorato36115782010-06-17 13:28:48 -04002395 private void onlyBindAllApps() {
2396 final Callbacks oldCallbacks = mCallbacks.get();
2397 if (oldCallbacks == null) {
2398 // This launcher has exited and nobody bothered to tell us. Just bail.
2399 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2400 return;
2401 }
2402
2403 // shallow copy
Winson Chungc208ff92012-03-29 17:37:41 -07002404 @SuppressWarnings("unchecked")
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002405 final ArrayList<AppInfo> list
2406 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
Winson Chungc93e5ae2012-07-23 20:48:26 -07002407 Runnable r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002408 public void run() {
2409 final long t = SystemClock.uptimeMillis();
2410 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2411 if (callbacks != null) {
2412 callbacks.bindAllApplications(list);
2413 }
2414 if (DEBUG_LOADERS) {
2415 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
2416 + (SystemClock.uptimeMillis()-t) + "ms");
2417 }
2418 }
Winson Chungc93e5ae2012-07-23 20:48:26 -07002419 };
2420 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
Winson Chung64359a52013-07-08 17:17:08 -07002421 if (isRunningOnMainThread) {
Winson Chungc93e5ae2012-07-23 20:48:26 -07002422 r.run();
2423 } else {
2424 mHandler.post(r);
2425 }
Joe Onorato36115782010-06-17 13:28:48 -04002426 }
2427
Winson Chung64359a52013-07-08 17:17:08 -07002428 private void loadAllApps() {
2429 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato36115782010-06-17 13:28:48 -04002430
Joe Onorato36115782010-06-17 13:28:48 -04002431 final Callbacks oldCallbacks = mCallbacks.get();
2432 if (oldCallbacks == null) {
2433 // This launcher has exited and nobody bothered to tell us. Just bail.
Winson Chung64359a52013-07-08 17:17:08 -07002434 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
Joe Onorato36115782010-06-17 13:28:48 -04002435 return;
2436 }
2437
Winson Chung64359a52013-07-08 17:17:08 -07002438 final PackageManager packageManager = mContext.getPackageManager();
Joe Onorato36115782010-06-17 13:28:48 -04002439 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2440 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2441
Winson Chung64359a52013-07-08 17:17:08 -07002442 // Clear the list of apps
2443 mBgAllAppsList.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002444
Winson Chung64359a52013-07-08 17:17:08 -07002445 // Query for the set of apps
2446 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2447 List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
2448 if (DEBUG_LOADERS) {
2449 Log.d(TAG, "queryIntentActivities took "
2450 + (SystemClock.uptimeMillis()-qiaTime) + "ms");
2451 Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps");
2452 }
2453 // Fail if we don't have any apps
2454 if (apps == null || apps.isEmpty()) {
2455 return;
2456 }
2457 // Sort the applications by name
2458 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2459 Collections.sort(apps,
2460 new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
2461 if (DEBUG_LOADERS) {
2462 Log.d(TAG, "sort took "
2463 + (SystemClock.uptimeMillis()-sortTime) + "ms");
Joe Onorato9c1289c2009-08-17 11:03:03 -04002464 }
2465
Winson Chung64359a52013-07-08 17:17:08 -07002466 // Create the ApplicationInfos
Winson Chung64359a52013-07-08 17:17:08 -07002467 for (int i = 0; i < apps.size(); i++) {
Bjorn Bringert85f418d2013-09-06 12:50:05 +01002468 ResolveInfo app = apps.get(i);
Bjorn Bringert1307f632013-10-03 22:31:03 +01002469 // This builds the icon bitmaps.
2470 mBgAllAppsList.add(new AppInfo(packageManager, app,
2471 mIconCache, mLabelCache));
Winson Chung64359a52013-07-08 17:17:08 -07002472 }
2473
Bjorn Bringert85f418d2013-09-06 12:50:05 +01002474 // Huh? Shouldn't this be inside the Runnable below?
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002475 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2476 mBgAllAppsList.added = new ArrayList<AppInfo>();
Winson Chung64359a52013-07-08 17:17:08 -07002477
2478 // Post callback on main thread
2479 mHandler.post(new Runnable() {
2480 public void run() {
2481 final long bindTime = SystemClock.uptimeMillis();
Winson Chung11a1a532013-09-13 11:14:45 -07002482 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Winson Chung64359a52013-07-08 17:17:08 -07002483 if (callbacks != null) {
2484 callbacks.bindAllApplications(added);
2485 if (DEBUG_LOADERS) {
2486 Log.d(TAG, "bound " + added.size() + " apps in "
2487 + (SystemClock.uptimeMillis() - bindTime) + "ms");
2488 }
2489 } else {
2490 Log.i(TAG, "not binding apps: no Launcher activity");
2491 }
2492 }
2493 });
2494
Joe Onorato36115782010-06-17 13:28:48 -04002495 if (DEBUG_LOADERS) {
Winson Chung64359a52013-07-08 17:17:08 -07002496 Log.d(TAG, "Icons processed in "
2497 + (SystemClock.uptimeMillis() - loadTime) + "ms");
Joe Onoratobe386092009-11-17 17:32:16 -08002498 }
2499 }
2500
2501 public void dumpState() {
Winson Chung2abf94d2012-07-18 18:16:38 -07002502 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002503 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2504 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2505 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2506 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2507 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2508 }
Joe Onorato36115782010-06-17 13:28:48 -04002509 }
2510 }
2511
2512 void enqueuePackageUpdated(PackageUpdatedTask task) {
Brad Fitzpatrick700889f2010-10-11 09:40:44 -07002513 sWorker.post(task);
Joe Onorato36115782010-06-17 13:28:48 -04002514 }
2515
2516 private class PackageUpdatedTask implements Runnable {
2517 int mOp;
2518 String[] mPackages;
2519
2520 public static final int OP_NONE = 0;
2521 public static final int OP_ADD = 1;
2522 public static final int OP_UPDATE = 2;
2523 public static final int OP_REMOVE = 3; // uninstlled
2524 public static final int OP_UNAVAILABLE = 4; // external media unmounted
2525
2526
2527 public PackageUpdatedTask(int op, String[] packages) {
2528 mOp = op;
2529 mPackages = packages;
2530 }
2531
2532 public void run() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -04002533 final Context context = mApp.getContext();
Joe Onorato36115782010-06-17 13:28:48 -04002534
2535 final String[] packages = mPackages;
2536 final int N = packages.length;
2537 switch (mOp) {
2538 case OP_ADD:
2539 for (int i=0; i<N; i++) {
2540 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
Adam Cohen487f7dd2012-06-28 18:12:10 -07002541 mBgAllAppsList.addPackage(context, packages[i]);
Joe Onorato36115782010-06-17 13:28:48 -04002542 }
2543 break;
2544 case OP_UPDATE:
2545 for (int i=0; i<N; i++) {
2546 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
Adam Cohen487f7dd2012-06-28 18:12:10 -07002547 mBgAllAppsList.updatePackage(context, packages[i]);
Michael Jurkaeb1bb922013-09-26 11:29:01 -07002548 WidgetPreviewLoader.removePackageFromDb(
Daniel Sandlere4f98912013-06-25 15:13:26 -04002549 mApp.getWidgetPreviewCacheDb(), packages[i]);
Joe Onorato36115782010-06-17 13:28:48 -04002550 }
2551 break;
2552 case OP_REMOVE:
2553 case OP_UNAVAILABLE:
2554 for (int i=0; i<N; i++) {
2555 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
Adam Cohen487f7dd2012-06-28 18:12:10 -07002556 mBgAllAppsList.removePackage(packages[i]);
Michael Jurkaeb1bb922013-09-26 11:29:01 -07002557 WidgetPreviewLoader.removePackageFromDb(
Daniel Sandlere4f98912013-06-25 15:13:26 -04002558 mApp.getWidgetPreviewCacheDb(), packages[i]);
Joe Onorato36115782010-06-17 13:28:48 -04002559 }
2560 break;
2561 }
2562
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002563 ArrayList<AppInfo> added = null;
2564 ArrayList<AppInfo> modified = null;
2565 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
Joe Onorato36115782010-06-17 13:28:48 -04002566
Adam Cohen487f7dd2012-06-28 18:12:10 -07002567 if (mBgAllAppsList.added.size() > 0) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002568 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
Winson Chung5d55f332012-07-16 20:45:03 -07002569 mBgAllAppsList.added.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002570 }
Adam Cohen487f7dd2012-06-28 18:12:10 -07002571 if (mBgAllAppsList.modified.size() > 0) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002572 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
Winson Chung5d55f332012-07-16 20:45:03 -07002573 mBgAllAppsList.modified.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002574 }
Winson Chung5d55f332012-07-16 20:45:03 -07002575 if (mBgAllAppsList.removed.size() > 0) {
Winson Chung83892cc2013-05-01 16:53:33 -07002576 removedApps.addAll(mBgAllAppsList.removed);
Winson Chung5d55f332012-07-16 20:45:03 -07002577 mBgAllAppsList.removed.clear();
Winson Chungcd810732012-06-18 16:45:43 -07002578 }
2579
Joe Onorato36115782010-06-17 13:28:48 -04002580 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
2581 if (callbacks == null) {
2582 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
2583 return;
2584 }
2585
2586 if (added != null) {
Winson Chung64359a52013-07-08 17:17:08 -07002587 // Ensure that we add all the workspace applications to the db
2588 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
Winson Chung94d67682013-09-25 16:29:40 -07002589 if (!AppsCustomizePagedView.DISABLE_ALL_APPS) {
2590 addAndBindAddedApps(context, new ArrayList<ItemInfo>(), cb, added);
2591 } else {
2592 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
2593 addAndBindAddedApps(context, addedInfos, cb, added);
2594 }
Joe Onorato36115782010-06-17 13:28:48 -04002595 }
2596 if (modified != null) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002597 final ArrayList<AppInfo> modifiedFinal = modified;
Winson Chung64359a52013-07-08 17:17:08 -07002598
2599 // Update the launcher db to reflect the changes
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002600 for (AppInfo a : modifiedFinal) {
Winson Chung64359a52013-07-08 17:17:08 -07002601 ArrayList<ItemInfo> infos =
2602 getItemInfoForComponentName(a.componentName);
2603 for (ItemInfo i : infos) {
2604 if (isShortcutInfoUpdateable(i)) {
2605 ShortcutInfo info = (ShortcutInfo) i;
2606 info.title = a.title.toString();
2607 updateItemInDatabase(context, info);
2608 }
2609 }
2610 }
2611
Joe Onorato36115782010-06-17 13:28:48 -04002612 mHandler.post(new Runnable() {
2613 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07002614 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2615 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04002616 callbacks.bindAppsUpdated(modifiedFinal);
2617 }
2618 }
2619 });
2620 }
Winson Chung83892cc2013-05-01 16:53:33 -07002621
Winson Chungdf95eb12013-10-16 14:57:07 -07002622 final ArrayList<String> removedPackageNames =
2623 new ArrayList<String>();
2624 if (mOp == OP_REMOVE) {
2625 // Mark all packages in the broadcast to be removed
2626 removedPackageNames.addAll(Arrays.asList(packages));
2627 } else if (mOp == OP_UPDATE) {
2628 // Mark disabled packages in the broadcast to be removed
2629 final PackageManager pm = context.getPackageManager();
2630 for (int i=0; i<N; i++) {
2631 if (isPackageDisabled(pm, packages[i])) {
2632 removedPackageNames.add(packages[i]);
Winson Chung64359a52013-07-08 17:17:08 -07002633 }
2634 }
Winson Chungdf95eb12013-10-16 14:57:07 -07002635 }
2636 // Remove all the components associated with this package
2637 for (String pn : removedPackageNames) {
2638 ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
2639 for (ItemInfo i : infos) {
2640 deleteItemFromDatabase(context, i);
2641 }
2642 }
2643 // Remove all the specific components
2644 for (AppInfo a : removedApps) {
2645 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName);
2646 for (ItemInfo i : infos) {
2647 deleteItemFromDatabase(context, i);
2648 }
2649 }
2650 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
2651 // Remove any queued items from the install queue
2652 String spKey = LauncherAppState.getSharedPreferencesKey();
2653 SharedPreferences sp =
2654 context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
2655 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
2656 // Call the components-removed callback
Joe Onorato36115782010-06-17 13:28:48 -04002657 mHandler.post(new Runnable() {
2658 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07002659 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2660 if (callbacks == cb && cb != null) {
Winson Chungdf95eb12013-10-16 14:57:07 -07002661 callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
Joe Onorato36115782010-06-17 13:28:48 -04002662 }
2663 }
2664 });
Joe Onoratobe386092009-11-17 17:32:16 -08002665 }
Winson Chung80baf5a2010-08-09 16:03:15 -07002666
Michael Jurkac402cd92013-05-20 15:49:32 +02002667 final ArrayList<Object> widgetsAndShortcuts =
2668 getSortedWidgetsAndShortcuts(context);
Winson Chung80baf5a2010-08-09 16:03:15 -07002669 mHandler.post(new Runnable() {
2670 @Override
2671 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07002672 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2673 if (callbacks == cb && cb != null) {
Michael Jurkac402cd92013-05-20 15:49:32 +02002674 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
Winson Chung80baf5a2010-08-09 16:03:15 -07002675 }
2676 }
2677 });
Adam Cohen4caf2982013-08-20 18:54:31 -07002678
2679 // Write all the logs to disk
Adam Cohen4caf2982013-08-20 18:54:31 -07002680 mHandler.post(new Runnable() {
2681 public void run() {
2682 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
2683 if (callbacks == cb && cb != null) {
Winson Chungede41292013-09-19 16:27:36 -07002684 callbacks.dumpLogsToLocalData();
Adam Cohen4caf2982013-08-20 18:54:31 -07002685 }
2686 }
2687 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04002688 }
2689 }
2690
Michael Jurkac402cd92013-05-20 15:49:32 +02002691 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
2692 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
2693 PackageManager packageManager = context.getPackageManager();
2694 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
2695 widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
2696 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
2697 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
2698 Collections.sort(widgetsAndShortcuts,
2699 new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
2700 return widgetsAndShortcuts;
2701 }
2702
Winson Chungdf95eb12013-10-16 14:57:07 -07002703 private boolean isPackageDisabled(PackageManager pm, String packageName) {
2704 try {
2705 PackageInfo pi = pm.getPackageInfo(packageName, 0);
2706 return !pi.applicationInfo.enabled;
2707 } catch (NameNotFoundException e) {
2708 // Fall through
2709 }
2710 return false;
2711 }
Winson Chung1323b482013-08-05 12:41:55 -07002712 private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
Winson Chungee055712013-07-30 14:46:24 -07002713 if (cn == null) {
2714 return false;
2715 }
Winson Chungdf95eb12013-10-16 14:57:07 -07002716 if (isPackageDisabled(pm, cn.getPackageName())) {
2717 return false;
2718 }
Winson Chungee055712013-07-30 14:46:24 -07002719
2720 try {
Winson Chungba9c37f2013-08-30 14:11:37 -07002721 // Check the activity
Winson Chungdf95eb12013-10-16 14:57:07 -07002722 PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
Winson Chungee055712013-07-30 14:46:24 -07002723 return (pm.getActivityInfo(cn, 0) != null);
2724 } catch (NameNotFoundException e) {
2725 return false;
2726 }
2727 }
2728
Joe Onorato9c1289c2009-08-17 11:03:03 -04002729 /**
Joe Onorato56d82912010-03-07 14:32:10 -05002730 * This is called from the code that adds shortcuts from the intent receiver. This
2731 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04002732 */
Joe Onorato56d82912010-03-07 14:32:10 -05002733 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
Winson Chungc3eecff2011-07-11 17:44:15 -07002734 return getShortcutInfo(manager, intent, context, null, -1, -1, null);
Joe Onorato56d82912010-03-07 14:32:10 -05002735 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002736
Joe Onorato56d82912010-03-07 14:32:10 -05002737 /**
2738 * Make an ShortcutInfo object for a shortcut that is an application.
2739 *
2740 * If c is not null, then it will be used to fill in missing data like the title and icon.
2741 */
2742 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
Winson Chungc3eecff2011-07-11 17:44:15 -07002743 Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
Joe Onorato56d82912010-03-07 14:32:10 -05002744 ComponentName componentName = intent.getComponent();
Winson Chung1323b482013-08-05 12:41:55 -07002745 final ShortcutInfo info = new ShortcutInfo();
Winson Chung68fd3c32013-08-30 16:38:00 -07002746 if (componentName != null && !isValidPackageComponent(manager, componentName)) {
Winson Chungee055712013-07-30 14:46:24 -07002747 Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07002748 return null;
Winson Chung1323b482013-08-05 12:41:55 -07002749 } else {
2750 try {
2751 PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
2752 info.initFlagsAndFirstInstallTime(pi);
2753 } catch (NameNotFoundException e) {
2754 Log.d(TAG, "getPackInfo failed for package " +
2755 componentName.getPackageName());
2756 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07002757 }
2758
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07002759 // TODO: See if the PackageManager knows about this case. If it doesn't
2760 // then return null & delete this.
2761
Joe Onorato56d82912010-03-07 14:32:10 -05002762 // the resource -- This may implicitly give us back the fallback icon,
2763 // but don't worry about that. All we're doing with usingFallbackIcon is
2764 // to avoid saving lots of copies of that in the database, and most apps
2765 // have icons anyway.
Winson Chungc208ff92012-03-29 17:37:41 -07002766
2767 // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
2768 // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
2769 // via resolveActivity().
Winson Chungee055712013-07-30 14:46:24 -07002770 Bitmap icon = null;
Winson Chungc208ff92012-03-29 17:37:41 -07002771 ResolveInfo resolveInfo = null;
2772 ComponentName oldComponent = intent.getComponent();
2773 Intent newIntent = new Intent(intent.getAction(), null);
2774 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2775 newIntent.setPackage(oldComponent.getPackageName());
2776 List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
2777 for (ResolveInfo i : infos) {
2778 ComponentName cn = new ComponentName(i.activityInfo.packageName,
2779 i.activityInfo.name);
2780 if (cn.equals(oldComponent)) {
2781 resolveInfo = i;
2782 }
2783 }
2784 if (resolveInfo == null) {
2785 resolveInfo = manager.resolveActivity(intent, 0);
2786 }
Joe Onorato56d82912010-03-07 14:32:10 -05002787 if (resolveInfo != null) {
Winson Chungaac01e12011-08-17 10:37:13 -07002788 icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07002789 }
Joe Onorato56d82912010-03-07 14:32:10 -05002790 // the db
2791 if (icon == null) {
2792 if (c != null) {
Michael Jurka931dc972011-08-05 15:08:15 -07002793 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05002794 }
2795 }
2796 // the fallback icon
2797 if (icon == null) {
2798 icon = getFallbackIcon();
2799 info.usingFallbackIcon = true;
2800 }
2801 info.setIcon(icon);
2802
2803 // from the resource
2804 if (resolveInfo != null) {
Winson Chung5308f242011-08-18 12:12:41 -07002805 ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
2806 if (labelCache != null && labelCache.containsKey(key)) {
2807 info.title = labelCache.get(key);
Winson Chungc3eecff2011-07-11 17:44:15 -07002808 } else {
2809 info.title = resolveInfo.activityInfo.loadLabel(manager);
2810 if (labelCache != null) {
Winson Chung5308f242011-08-18 12:12:41 -07002811 labelCache.put(key, info.title);
Winson Chungc3eecff2011-07-11 17:44:15 -07002812 }
2813 }
Joe Onorato56d82912010-03-07 14:32:10 -05002814 }
2815 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04002816 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05002817 if (c != null) {
2818 info.title = c.getString(titleIndex);
2819 }
2820 }
2821 // fall back to the class name of the activity
2822 if (info.title == null) {
2823 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07002824 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002825 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
2826 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002827 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07002828
Winson Chung64359a52013-07-08 17:17:08 -07002829 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
2830 ItemInfoFilter f) {
2831 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
2832 for (ItemInfo i : infos) {
2833 if (i instanceof ShortcutInfo) {
2834 ShortcutInfo info = (ShortcutInfo) i;
2835 ComponentName cn = info.intent.getComponent();
2836 if (cn != null && f.filterItem(null, info, cn)) {
2837 filtered.add(info);
2838 }
2839 } else if (i instanceof FolderInfo) {
2840 FolderInfo info = (FolderInfo) i;
2841 for (ShortcutInfo s : info.contents) {
2842 ComponentName cn = s.intent.getComponent();
2843 if (cn != null && f.filterItem(info, s, cn)) {
2844 filtered.add(s);
Winson Chung8a435102012-08-30 17:16:53 -07002845 }
2846 }
Winson Chung64359a52013-07-08 17:17:08 -07002847 } else if (i instanceof LauncherAppWidgetInfo) {
2848 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
2849 ComponentName cn = info.providerName;
2850 if (cn != null && f.filterItem(null, info, cn)) {
2851 filtered.add(info);
2852 }
Winson Chung8a435102012-08-30 17:16:53 -07002853 }
2854 }
Winson Chung64359a52013-07-08 17:17:08 -07002855 return new ArrayList<ItemInfo>(filtered);
2856 }
2857
2858 private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
Winson Chung64359a52013-07-08 17:17:08 -07002859 ItemInfoFilter filter = new ItemInfoFilter() {
2860 @Override
2861 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
2862 return cn.getPackageName().equals(pn);
2863 }
2864 };
2865 return filterItemInfos(sBgItemsIdMap.values(), filter);
2866 }
2867
2868 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
Winson Chung64359a52013-07-08 17:17:08 -07002869 ItemInfoFilter filter = new ItemInfoFilter() {
2870 @Override
2871 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
2872 return cn.equals(cname);
2873 }
2874 };
2875 return filterItemInfos(sBgItemsIdMap.values(), filter);
2876 }
2877
2878 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
2879 if (i instanceof ShortcutInfo) {
2880 ShortcutInfo info = (ShortcutInfo) i;
2881 // We need to check for ACTION_MAIN otherwise getComponent() might
2882 // return null for some shortcuts (for instance, for shortcuts to
2883 // web pages.)
2884 Intent intent = info.intent;
2885 ComponentName name = intent.getComponent();
2886 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
2887 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
2888 return true;
2889 }
2890 }
2891 return false;
Winson Chung8a435102012-08-30 17:16:53 -07002892 }
2893
2894 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08002895 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002896 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08002897 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05002898 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
2899 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002900
Joe Onorato56d82912010-03-07 14:32:10 -05002901 Bitmap icon = null;
Michael Jurkac9d95c52011-08-29 14:03:34 -07002902 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04002903 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002904
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07002905 // TODO: If there's an explicit component and we can't install that, delete it.
2906
Joe Onorato56d82912010-03-07 14:32:10 -05002907 info.title = c.getString(titleIndex);
2908
Joe Onorato9c1289c2009-08-17 11:03:03 -04002909 int iconType = c.getInt(iconTypeIndex);
2910 switch (iconType) {
2911 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
2912 String packageName = c.getString(iconPackageIndex);
2913 String resourceName = c.getString(iconResourceIndex);
2914 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05002915 info.customIcon = false;
2916 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002917 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04002918 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05002919 if (resources != null) {
2920 final int id = resources.getIdentifier(resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07002921 icon = Utilities.createIconBitmap(
2922 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato56d82912010-03-07 14:32:10 -05002923 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002924 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05002925 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002926 }
Joe Onorato56d82912010-03-07 14:32:10 -05002927 // the db
2928 if (icon == null) {
Michael Jurka931dc972011-08-05 15:08:15 -07002929 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05002930 }
2931 // the fallback icon
2932 if (icon == null) {
2933 icon = getFallbackIcon();
2934 info.usingFallbackIcon = true;
2935 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002936 break;
2937 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Michael Jurka931dc972011-08-05 15:08:15 -07002938 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05002939 if (icon == null) {
2940 icon = getFallbackIcon();
2941 info.customIcon = false;
2942 info.usingFallbackIcon = true;
2943 } else {
2944 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04002945 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002946 break;
2947 default:
Joe Onoratod8d22da2010-03-11 17:59:11 -08002948 icon = getFallbackIcon();
Joe Onorato56d82912010-03-07 14:32:10 -05002949 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04002950 info.customIcon = false;
2951 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002952 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08002953 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002954 return info;
2955 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002956
Michael Jurka931dc972011-08-05 15:08:15 -07002957 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
Michael Jurka3a9fced2012-04-13 14:44:29 -07002958 @SuppressWarnings("all") // suppress dead code warning
2959 final boolean debug = false;
2960 if (debug) {
Joe Onorato56d82912010-03-07 14:32:10 -05002961 Log.d(TAG, "getIconFromCursor app="
2962 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
2963 }
2964 byte[] data = c.getBlob(iconIndex);
2965 try {
Michael Jurka931dc972011-08-05 15:08:15 -07002966 return Utilities.createIconBitmap(
2967 BitmapFactory.decodeByteArray(data, 0, data.length), context);
Joe Onorato56d82912010-03-07 14:32:10 -05002968 } catch (Exception e) {
2969 return null;
2970 }
2971 }
2972
Winson Chung3d503fb2011-07-13 17:25:49 -07002973 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
2974 int cellX, int cellY, boolean notify) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07002975 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
Adam Cohend9198822011-11-22 16:42:47 -08002976 if (info == null) {
2977 return null;
2978 }
Winson Chung3d503fb2011-07-13 17:25:49 -07002979 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
Joe Onorato0589f0f2010-02-08 13:44:00 -08002980
2981 return info;
2982 }
2983
Winson Chunga9abd0e2010-10-27 17:18:37 -07002984 /**
Winson Chung55cef262010-10-28 14:14:18 -07002985 * Attempts to find an AppWidgetProviderInfo that matches the given component.
2986 */
2987 AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
2988 ComponentName component) {
2989 List<AppWidgetProviderInfo> widgets =
2990 AppWidgetManager.getInstance(context).getInstalledProviders();
2991 for (AppWidgetProviderInfo info : widgets) {
2992 if (info.provider.equals(component)) {
2993 return info;
2994 }
2995 }
2996 return null;
Winson Chunga9abd0e2010-10-27 17:18:37 -07002997 }
2998
Winson Chung68846fd2010-10-29 11:00:27 -07002999 /**
3000 * Returns a list of all the widgets that can handle configuration with a particular mimeType.
3001 */
3002 List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
3003 final PackageManager packageManager = context.getPackageManager();
3004 final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
3005 new ArrayList<WidgetMimeTypeHandlerData>();
3006
3007 final Intent supportsIntent =
3008 new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
3009 supportsIntent.setType(mimeType);
3010
3011 // Create a set of widget configuration components that we can test against
3012 final List<AppWidgetProviderInfo> widgets =
3013 AppWidgetManager.getInstance(context).getInstalledProviders();
3014 final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
3015 new HashMap<ComponentName, AppWidgetProviderInfo>();
3016 for (AppWidgetProviderInfo info : widgets) {
3017 configurationComponentToWidget.put(info.configure, info);
3018 }
3019
3020 // Run through each of the intents that can handle this type of clip data, and cross
3021 // reference them with the components that are actual configuration components
3022 final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
3023 PackageManager.MATCH_DEFAULT_ONLY);
3024 for (ResolveInfo info : activities) {
3025 final ActivityInfo activityInfo = info.activityInfo;
3026 final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
3027 activityInfo.name);
3028 if (configurationComponentToWidget.containsKey(infoComponent)) {
3029 supportedConfigurationActivities.add(
3030 new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
3031 configurationComponentToWidget.get(infoComponent)));
3032 }
3033 }
3034 return supportedConfigurationActivities;
3035 }
3036
Winson Chunga9abd0e2010-10-27 17:18:37 -07003037 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003038 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3039 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3040 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3041
Adam Cohend9198822011-11-22 16:42:47 -08003042 if (intent == null) {
3043 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3044 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3045 return null;
3046 }
3047
Joe Onorato0589f0f2010-02-08 13:44:00 -08003048 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003049 boolean customIcon = false;
3050 ShortcutIconResource iconResource = null;
3051
3052 if (bitmap != null && bitmap instanceof Bitmap) {
3053 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003054 customIcon = true;
3055 } else {
3056 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3057 if (extra != null && extra instanceof ShortcutIconResource) {
3058 try {
3059 iconResource = (ShortcutIconResource) extra;
3060 final PackageManager packageManager = context.getPackageManager();
3061 Resources resources = packageManager.getResourcesForApplication(
3062 iconResource.packageName);
3063 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07003064 icon = Utilities.createIconBitmap(
3065 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003066 } catch (Exception e) {
3067 Log.w(TAG, "Could not load shortcut icon: " + extra);
3068 }
3069 }
3070 }
3071
Michael Jurkac9d95c52011-08-29 14:03:34 -07003072 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05003073
3074 if (icon == null) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07003075 if (fallbackIcon != null) {
3076 icon = fallbackIcon;
3077 } else {
3078 icon = getFallbackIcon();
3079 info.usingFallbackIcon = true;
3080 }
Joe Onorato56d82912010-03-07 14:32:10 -05003081 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08003082 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05003083
Joe Onorato0589f0f2010-02-08 13:44:00 -08003084 info.title = name;
3085 info.intent = intent;
3086 info.customIcon = customIcon;
3087 info.iconResource = iconResource;
3088
3089 return info;
3090 }
3091
Winson Chungaac01e12011-08-17 10:37:13 -07003092 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
3093 int iconIndex) {
Joe Onorato17a89222011-02-08 17:26:11 -08003094 // If apps can't be on SD, don't even bother.
Winson Chungee055712013-07-30 14:46:24 -07003095 if (!mAppsCanBeOnRemoveableStorage) {
Winson Chungaac01e12011-08-17 10:37:13 -07003096 return false;
Joe Onorato17a89222011-02-08 17:26:11 -08003097 }
Joe Onorato56d82912010-03-07 14:32:10 -05003098 // If this icon doesn't have a custom icon, check to see
3099 // what's stored in the DB, and if it doesn't match what
3100 // we're going to show, store what we are going to show back
3101 // into the DB. We do this so when we're loading, if the
3102 // package manager can't find an icon (for example because
3103 // the app is on SD) then we can use that instead.
Joe Onoratoddc9c1f2010-08-30 18:30:15 -07003104 if (!info.customIcon && !info.usingFallbackIcon) {
Winson Chungaac01e12011-08-17 10:37:13 -07003105 cache.put(info, c.getBlob(iconIndex));
3106 return true;
3107 }
3108 return false;
3109 }
3110 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3111 boolean needSave = false;
3112 try {
3113 if (data != null) {
3114 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3115 Bitmap loaded = info.getIcon(mIconCache);
3116 needSave = !saved.sameAs(loaded);
3117 } else {
Joe Onorato56d82912010-03-07 14:32:10 -05003118 needSave = true;
3119 }
Winson Chungaac01e12011-08-17 10:37:13 -07003120 } catch (Exception e) {
3121 needSave = true;
3122 }
3123 if (needSave) {
3124 Log.d(TAG, "going to save icon bitmap for info=" + info);
3125 // This is slower than is ideal, but this only happens once
3126 // or when the app is updated with a new icon.
3127 updateItemInDatabase(context, info);
Joe Onorato56d82912010-03-07 14:32:10 -05003128 }
3129 }
3130
Joe Onorato9c1289c2009-08-17 11:03:03 -04003131 /**
Adam Cohendf2cc412011-04-27 16:56:57 -07003132 * Return an existing FolderInfo object if we have encountered this ID previously,
Joe Onorato9c1289c2009-08-17 11:03:03 -04003133 * or make a new one.
3134 */
Adam Cohendf2cc412011-04-27 16:56:57 -07003135 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003136 // See if a placeholder was created for us already
3137 FolderInfo folderInfo = folders.get(id);
Adam Cohendf2cc412011-04-27 16:56:57 -07003138 if (folderInfo == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003139 // No placeholder -- create a new instance
Michael Jurkac9d95c52011-08-29 14:03:34 -07003140 folderInfo = new FolderInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04003141 folders.put(id, folderInfo);
3142 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003143 return folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003144 }
3145
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003146 public static final Comparator<AppInfo> getAppNameComparator() {
Winson Chung11904872012-09-17 16:58:46 -07003147 final Collator collator = Collator.getInstance();
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003148 return new Comparator<AppInfo>() {
3149 public final int compare(AppInfo a, AppInfo b) {
Winson Chung780fe592013-09-26 14:48:44 -07003150 int result = collator.compare(a.title.toString().trim(),
3151 b.title.toString().trim());
Winson Chung11904872012-09-17 16:58:46 -07003152 if (result == 0) {
3153 result = a.componentName.compareTo(b.componentName);
3154 }
3155 return result;
Michael Jurka5b1808d2011-07-11 19:59:46 -07003156 }
Winson Chung11904872012-09-17 16:58:46 -07003157 };
3158 }
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003159 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
3160 = new Comparator<AppInfo>() {
3161 public final int compare(AppInfo a, AppInfo b) {
Winson Chung78403fe2011-01-21 15:38:02 -08003162 if (a.firstInstallTime < b.firstInstallTime) return 1;
3163 if (a.firstInstallTime > b.firstInstallTime) return -1;
3164 return 0;
3165 }
3166 };
Winson Chung11904872012-09-17 16:58:46 -07003167 public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() {
3168 final Collator collator = Collator.getInstance();
3169 return new Comparator<AppWidgetProviderInfo>() {
3170 public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
Winson Chung780fe592013-09-26 14:48:44 -07003171 return collator.compare(a.label.toString().trim(), b.label.toString().trim());
Winson Chung11904872012-09-17 16:58:46 -07003172 }
3173 };
3174 }
Winson Chung5308f242011-08-18 12:12:41 -07003175 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3176 if (info.activityInfo != null) {
3177 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3178 } else {
3179 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3180 }
3181 }
Winson Chung785d2eb2011-04-14 16:08:02 -07003182 public static class ShortcutNameComparator implements Comparator<ResolveInfo> {
Winson Chung11904872012-09-17 16:58:46 -07003183 private Collator mCollator;
Winson Chung785d2eb2011-04-14 16:08:02 -07003184 private PackageManager mPackageManager;
Winson Chungc3eecff2011-07-11 17:44:15 -07003185 private HashMap<Object, CharSequence> mLabelCache;
Winson Chung785d2eb2011-04-14 16:08:02 -07003186 ShortcutNameComparator(PackageManager pm) {
3187 mPackageManager = pm;
Winson Chungc3eecff2011-07-11 17:44:15 -07003188 mLabelCache = new HashMap<Object, CharSequence>();
Winson Chung11904872012-09-17 16:58:46 -07003189 mCollator = Collator.getInstance();
Winson Chungc3eecff2011-07-11 17:44:15 -07003190 }
3191 ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) {
3192 mPackageManager = pm;
3193 mLabelCache = labelCache;
Winson Chung11904872012-09-17 16:58:46 -07003194 mCollator = Collator.getInstance();
Winson Chung785d2eb2011-04-14 16:08:02 -07003195 }
3196 public final int compare(ResolveInfo a, ResolveInfo b) {
Winson Chungc3eecff2011-07-11 17:44:15 -07003197 CharSequence labelA, labelB;
Winson Chung5308f242011-08-18 12:12:41 -07003198 ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
3199 ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
3200 if (mLabelCache.containsKey(keyA)) {
3201 labelA = mLabelCache.get(keyA);
Winson Chungc3eecff2011-07-11 17:44:15 -07003202 } else {
Winson Chung780fe592013-09-26 14:48:44 -07003203 labelA = a.loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003204
Winson Chung5308f242011-08-18 12:12:41 -07003205 mLabelCache.put(keyA, labelA);
Winson Chungc3eecff2011-07-11 17:44:15 -07003206 }
Winson Chung5308f242011-08-18 12:12:41 -07003207 if (mLabelCache.containsKey(keyB)) {
3208 labelB = mLabelCache.get(keyB);
Winson Chungc3eecff2011-07-11 17:44:15 -07003209 } else {
Winson Chung780fe592013-09-26 14:48:44 -07003210 labelB = b.loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003211
Winson Chung5308f242011-08-18 12:12:41 -07003212 mLabelCache.put(keyB, labelB);
Winson Chungc3eecff2011-07-11 17:44:15 -07003213 }
Winson Chung11904872012-09-17 16:58:46 -07003214 return mCollator.compare(labelA, labelB);
Winson Chung785d2eb2011-04-14 16:08:02 -07003215 }
3216 };
Winson Chung1ed747a2011-05-03 16:18:34 -07003217 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
Winson Chung11904872012-09-17 16:58:46 -07003218 private Collator mCollator;
Winson Chung1ed747a2011-05-03 16:18:34 -07003219 private PackageManager mPackageManager;
3220 private HashMap<Object, String> mLabelCache;
3221 WidgetAndShortcutNameComparator(PackageManager pm) {
3222 mPackageManager = pm;
3223 mLabelCache = new HashMap<Object, String>();
Winson Chung11904872012-09-17 16:58:46 -07003224 mCollator = Collator.getInstance();
Winson Chung1ed747a2011-05-03 16:18:34 -07003225 }
3226 public final int compare(Object a, Object b) {
3227 String labelA, labelB;
Winson Chungc3eecff2011-07-11 17:44:15 -07003228 if (mLabelCache.containsKey(a)) {
3229 labelA = mLabelCache.get(a);
3230 } else {
3231 labelA = (a instanceof AppWidgetProviderInfo) ?
Winson Chung1ed747a2011-05-03 16:18:34 -07003232 ((AppWidgetProviderInfo) a).label :
Winson Chung780fe592013-09-26 14:48:44 -07003233 ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003234 mLabelCache.put(a, labelA);
3235 }
3236 if (mLabelCache.containsKey(b)) {
3237 labelB = mLabelCache.get(b);
3238 } else {
3239 labelB = (b instanceof AppWidgetProviderInfo) ?
Winson Chung1ed747a2011-05-03 16:18:34 -07003240 ((AppWidgetProviderInfo) b).label :
Winson Chung780fe592013-09-26 14:48:44 -07003241 ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003242 mLabelCache.put(b, labelB);
3243 }
Winson Chung11904872012-09-17 16:58:46 -07003244 return mCollator.compare(labelA, labelB);
Winson Chung1ed747a2011-05-03 16:18:34 -07003245 }
3246 };
Joe Onoratobe386092009-11-17 17:32:16 -08003247
3248 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08003249 Log.d(TAG, "mCallbacks=" + mCallbacks);
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003250 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3251 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3252 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3253 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
Joe Onorato36115782010-06-17 13:28:48 -04003254 if (mLoaderTask != null) {
3255 mLoaderTask.dumpState();
3256 } else {
3257 Log.d(TAG, "mLoaderTask=null");
3258 }
Joe Onoratobe386092009-11-17 17:32:16 -08003259 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003260}