blob: 404ab9a112d670481de1981c470abc4a364b38c2 [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;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070022import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.ContentProviderClient;
25import android.content.ContentProviderOperation;
26import android.content.ContentResolver;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080030import android.content.Intent.ShortcutIconResource;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070031import android.content.IntentFilter;
32import android.content.SharedPreferences;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.pm.PackageManager;
Jason Monkbbe1e242014-05-16 17:37:34 -040034import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.content.pm.ResolveInfo;
Reena Lee93f824a2011-09-23 17:20:28 -070036import android.content.res.Configuration;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.content.res.Resources;
38import android.database.Cursor;
39import android.graphics.Bitmap;
40import android.graphics.BitmapFactory;
41import android.net.Uri;
Joe Onorato17a89222011-02-08 17:26:11 -080042import android.os.Environment;
Joe Onorato36115782010-06-17 13:28:48 -040043import android.os.Handler;
44import android.os.HandlerThread;
Joe Onorato0589f0f2010-02-08 13:44:00 -080045import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046import android.os.Process;
Winson Chungaafa03c2010-06-11 17:34:16 -070047import android.os.RemoteException;
Joe Onorato9c1289c2009-08-17 11:03:03 -040048import android.os.SystemClock;
Chris Wrenc3919c02013-09-18 09:48:33 -040049import android.provider.BaseColumns;
Winson Chunga90303b2013-11-15 13:05:06 -080050import android.text.TextUtils;
Winson Chungaafa03c2010-06-11 17:34:16 -070051import android.util.Log;
Winson Chungc9168342013-06-26 14:54:55 -070052import android.util.Pair;
Michael Jurka34c2e6c2013-12-13 16:07:45 +010053
Sunny Goyalffe83f12014-08-14 17:39:34 -070054import com.android.launcher3.compat.AppWidgetManagerCompat;
Kenny Guyed131872014-04-30 03:02:21 +010055import com.android.launcher3.compat.LauncherActivityInfoCompat;
56import com.android.launcher3.compat.LauncherAppsCompat;
Sunny Goyal34942622014-08-29 17:20:55 -070057import com.android.launcher3.compat.PackageInstallerCompat;
Sunny Goyale755d462014-07-22 13:48:29 -070058import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
Kenny Guyed131872014-04-30 03:02:21 +010059import com.android.launcher3.compat.UserHandleCompat;
60import com.android.launcher3.compat.UserManagerCompat;
Romain Guyedcce092010-03-04 13:03:17 -080061
Michael Jurkac2f801e2011-07-12 14:19:46 -070062import java.lang.ref.WeakReference;
63import java.net.URISyntaxException;
Sunny Goyal34942622014-08-29 17:20:55 -070064import java.security.InvalidParameterException;
Michael Jurkac2f801e2011-07-12 14:19:46 -070065import java.text.Collator;
66import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070067import java.util.Arrays;
Winson Chung64359a52013-07-08 17:17:08 -070068import java.util.Collection;
Michael Jurkac2f801e2011-07-12 14:19:46 -070069import java.util.Collections;
70import java.util.Comparator;
71import java.util.HashMap;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070072import java.util.HashSet;
Winson Chung2abf94d2012-07-18 18:16:38 -070073import java.util.Iterator;
Michael Jurkac2f801e2011-07-12 14:19:46 -070074import java.util.List;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070075import java.util.Map.Entry;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070076import java.util.Set;
Adam Cohendcd297f2013-06-18 13:13:40 -070077import java.util.TreeMap;
Winson Chunga0b7e862013-09-05 16:03:15 -070078import java.util.concurrent.atomic.AtomicBoolean;
Michael Jurkac2f801e2011-07-12 14:19:46 -070079
The Android Open Source Project31dd5032009-03-03 19:32:27 -080080/**
81 * Maintains in-memory state of the Launcher. It is expected that there should be only one
82 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070083 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084 */
Kenny Guyed131872014-04-30 03:02:21 +010085public class LauncherModel extends BroadcastReceiver
Kenny Guyc2bd8102014-06-30 12:30:31 +010086 implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080087 static final boolean DEBUG_LOADERS = false;
Chris Wrenee523362014-09-09 10:09:02 -040088 private static final boolean DEBUG_RECEIVER = false;
Sunny Goyal94485362014-09-18 16:13:58 -070089 private static final boolean REMOVE_UNRESTORED_ICONS = true;
Chris Wrenb358f812014-04-16 13:37:00 -040090
Joe Onorato9c1289c2009-08-17 11:03:03 -040091 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070092
Daniel Sandler8707e0f2013-08-15 15:54:18 -070093 // true = use a "More Apps" folder for non-workspace apps on upgrade
94 // false = strew non-workspace apps across the workspace on upgrade
95 public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
Dan Sandlerd5024042014-01-09 15:01:33 -050096 public static final int LOADER_FLAG_NONE = 0;
97 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
98 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
99
Joe Onorato36115782010-06-17 13:28:48 -0400100 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
Derek Prothro7aff3992013-12-10 14:00:37 -0500101 private static final long INVALID_SCREEN_ID = -1L;
Winson Chunga6945242014-01-08 14:04:34 -0800102
Winson Chungee055712013-07-30 14:46:24 -0700103 private final boolean mAppsCanBeOnRemoveableStorage;
Winson Chunga6945242014-01-08 14:04:34 -0800104 private final boolean mOldContentProviderExists;
Daniel Sandlerdca66122010-04-13 16:23:58 -0400105
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400106 private final LauncherAppState mApp;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400107 private final Object mLock = new Object();
108 private DeferredHandler mHandler = new DeferredHandler();
Joe Onorato36115782010-06-17 13:28:48 -0400109 private LoaderTask mLoaderTask;
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700110 private boolean mIsLoaderTaskRunning;
Michael Jurkac7700af2013-05-14 20:17:58 +0200111 private volatile boolean mFlushingWorkerThread;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800112
Winson Chung81b52252012-08-27 15:34:29 -0700113 // Specific runnable types that are run on the main thread deferred handler, this allows us to
114 // clear all queued binding runnables when the Launcher activity is destroyed.
115 private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
116 private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
117
Jason Monkbbe1e242014-05-16 17:37:34 -0400118 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
Winson Chung81b52252012-08-27 15:34:29 -0700119
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700120 private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
121 static {
122 sWorkerThread.start();
123 }
124 private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
125
Joe Onoratocc67f472010-06-08 10:54:30 -0700126 // We start off with everything not loaded. After that, we assume that
127 // our monitoring of the package manager provides all updates and we never
128 // need to do a requery. These are only ever touched from the loader thread.
129 private boolean mWorkspaceLoaded;
130 private boolean mAllAppsLoaded;
131
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700132 // When we are loading pages synchronously, we can't just post the binding of items on the side
133 // pages as this delays the rotation process. Instead, we wait for a callback from the first
134 // draw (in Workspace) to initiate the binding of the remaining side pages. Any time we start
135 // a normal load, we also clear this set of Runnables.
136 static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
137
Joe Onorato9c1289c2009-08-17 11:03:03 -0400138 private WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800139
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700140 // < only access in worker thread >
Adam Cohen4caf2982013-08-20 18:54:31 -0700141 AllAppsList mBgAllAppsList;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800142
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700143 // The lock that must be acquired before referencing any static bg data structures. Unlike
144 // other locks, this one can generally be held long-term because we never expect any of these
145 // static data structures to be referenced outside of the worker thread except on the first
146 // load after configuration change.
Winson Chung2abf94d2012-07-18 18:16:38 -0700147 static final Object sBgLock = new Object();
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700148
Adam Cohen487f7dd2012-06-28 18:12:10 -0700149 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700150 // LauncherModel to their ids
Adam Cohen487f7dd2012-06-28 18:12:10 -0700151 static final HashMap<Long, ItemInfo> sBgItemsIdMap = new HashMap<Long, ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700152
Adam Cohen487f7dd2012-06-28 18:12:10 -0700153 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
154 // created by LauncherModel that are directly on the home screen (however, no widgets or
155 // shortcuts within folders).
156 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700157
Adam Cohen487f7dd2012-06-28 18:12:10 -0700158 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
159 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700160 new ArrayList<LauncherAppWidgetInfo>();
161
Adam Cohen487f7dd2012-06-28 18:12:10 -0700162 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
163 static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
Winson Chungb1094bd2011-08-24 16:14:08 -0700164
Adam Cohen487f7dd2012-06-28 18:12:10 -0700165 // sBgDbIconCache is the set of ItemInfos that need to have their icons updated in the database
166 static final HashMap<Object, byte[]> sBgDbIconCache = new HashMap<Object, byte[]>();
Adam Cohendcd297f2013-06-18 13:13:40 -0700167
168 // sBgWorkspaceScreens is the ordered set of workspace screens.
169 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
170
Sunny Goyalf599ccf2014-07-08 13:01:29 -0700171 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
Sameer Padala513edae2014-07-29 16:17:08 -0700172 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
173 new HashMap<UserHandleCompat, HashSet<String>>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -0700174
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700175 // </ only access in worker thread >
176
177 private IconCache mIconCache;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800178
Reena Lee99a73f32011-10-24 17:27:37 -0700179 protected int mPreviousConfigMcc;
Reena Lee93f824a2011-09-23 17:20:28 -0700180
Kenny Guyed131872014-04-30 03:02:21 +0100181 private final LauncherAppsCompat mLauncherApps;
182 private final UserManagerCompat mUserManager;
183
Joe Onorato9c1289c2009-08-17 11:03:03 -0400184 public interface Callbacks {
Joe Onoratoef2efcf2010-10-27 13:21:00 -0700185 public boolean setLoadOnResume();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400186 public int getCurrentWorkspaceScreen();
187 public void startBinding();
Winson Chung64359a52013-07-08 17:17:08 -0700188 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
189 boolean forceAnimateIcons);
Adam Cohendcd297f2013-06-18 13:13:40 -0700190 public void bindScreens(ArrayList<Long> orderedScreenIds);
Winson Chung64359a52013-07-08 17:17:08 -0700191 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
Joe Onoratoad72e172009-11-06 16:25:04 -0500192 public void bindFolders(HashMap<Long,FolderInfo> folders);
Adam Cohene25af792013-06-06 23:08:25 -0700193 public void finishBindingItems(boolean upgradePath);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400194 public void bindAppWidget(LauncherAppWidgetInfo info);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200195 public void bindAllApplications(ArrayList<AppInfo> apps);
Winson Chungd64d1762013-08-20 14:37:16 -0700196 public void bindAppsAdded(ArrayList<Long> newScreens,
197 ArrayList<ItemInfo> addNotAnimated,
Winson Chungc58497e2013-09-03 17:48:37 -0700198 ArrayList<ItemInfo> addAnimated,
199 ArrayList<AppInfo> addedApps);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200200 public void bindAppsUpdated(ArrayList<AppInfo> apps);
Sunny Goyale755d462014-07-22 13:48:29 -0700201 public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
Winson Chung83892cc2013-05-01 16:53:33 -0700202 public void bindComponentsRemoved(ArrayList<String> packageNames,
Kenny Guyed131872014-04-30 03:02:21 +0100203 ArrayList<AppInfo> appInfos, UserHandleCompat user);
Michael Jurkac402cd92013-05-20 15:49:32 +0200204 public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
Narayan Kamathcb1a4772011-06-28 13:46:59 +0100205 public void bindSearchablesChanged();
Winson Chunga0b7e862013-09-05 16:03:15 -0700206 public boolean isAllAppsButtonRank(int rank);
Adam Cohen1462de32012-07-24 22:34:36 -0700207 public void onPageBoundSynchronously(int page);
Winson Chungede41292013-09-19 16:27:36 -0700208 public void dumpLogsToLocalData();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400209 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800210
Winson Chung64359a52013-07-08 17:17:08 -0700211 public interface ItemInfoFilter {
212 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
213 }
214
Bjorn Bringert1307f632013-10-03 22:31:03 +0100215 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Winson Chunga6945242014-01-08 14:04:34 -0800216 Context context = app.getContext();
Daniel Sandlere4f98912013-06-25 15:13:26 -0400217
Winson Chungee055712013-07-30 14:46:24 -0700218 mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
Adam Cohen71483f42014-05-15 14:04:01 -0700219 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
Jason Monkbbe1e242014-05-16 17:37:34 -0400220 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
221 // resource string.
222 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
223 ProviderInfo providerInfo =
224 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
225 ProviderInfo redirectProvider =
226 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
Adam Cohen71483f42014-05-15 14:04:01 -0700227
228 Log.d(TAG, "Old launcher provider: " + oldProvider);
Jason Monkbbe1e242014-05-16 17:37:34 -0400229 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
Adam Cohen71483f42014-05-15 14:04:01 -0700230
231 if (mOldContentProviderExists) {
232 Log.d(TAG, "Old launcher provider exists.");
233 } else {
234 Log.d(TAG, "Old launcher provider does not exist.");
235 }
236
Daniel Sandlere4f98912013-06-25 15:13:26 -0400237 mApp = app;
Bjorn Bringert1307f632013-10-03 22:31:03 +0100238 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800239 mIconCache = iconCache;
240
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400241 final Resources res = context.getResources();
Reena Lee99a73f32011-10-24 17:27:37 -0700242 Configuration config = res.getConfiguration();
243 mPreviousConfigMcc = config.mcc;
Kenny Guyed131872014-04-30 03:02:21 +0100244 mLauncherApps = LauncherAppsCompat.getInstance(context);
245 mUserManager = UserManagerCompat.getInstance(context);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800246 }
247
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700248 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
249 * posted on the main thread handler. */
250 private void runOnMainThread(Runnable r) {
Winson Chung81b52252012-08-27 15:34:29 -0700251 runOnMainThread(r, 0);
252 }
253 private void runOnMainThread(Runnable r, int type) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700254 if (sWorkerThread.getThreadId() == Process.myTid()) {
255 // If we are on the worker thread, post onto the main handler
256 mHandler.post(r);
257 } else {
258 r.run();
259 }
260 }
261
262 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
263 * posted on the worker thread handler. */
264 private static void runOnWorkerThread(Runnable r) {
265 if (sWorkerThread.getThreadId() == Process.myTid()) {
266 r.run();
267 } else {
268 // If we are not on the worker thread, then post to the worker handler
269 sWorker.post(r);
270 }
271 }
272
Winson Chunge43a1e72014-01-15 10:33:02 -0800273 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
274 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
Winson Chunga6945242014-01-08 14:04:34 -0800275 }
276
Winson Chungc9168342013-06-26 14:54:55 -0700277 static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
278 long screen) {
Winson Chung892c74d2013-08-22 16:15:50 -0700279 LauncherAppState app = LauncherAppState.getInstance();
280 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
281 final int xCount = (int) grid.numColumns;
282 final int yCount = (int) grid.numRows;
Winson Chungc9168342013-06-26 14:54:55 -0700283 boolean[][] occupied = new boolean[xCount][yCount];
284
285 int cellX, cellY, spanX, spanY;
286 for (int i = 0; i < items.size(); ++i) {
287 final ItemInfo item = items.get(i);
288 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
289 if (item.screenId == screen) {
290 cellX = item.cellX;
291 cellY = item.cellY;
292 spanX = item.spanX;
293 spanY = item.spanY;
294 for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
295 for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
296 occupied[x][y] = true;
297 }
298 }
299 }
300 }
301 }
302
303 return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
304 }
305 static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
Winson Chung156ab5b2013-07-12 14:14:16 -0700306 Intent launchIntent,
Winson Chung76828c82013-08-19 15:43:29 -0700307 int firstScreenIndex,
308 ArrayList<Long> workspaceScreens) {
Winson Chungc9168342013-06-26 14:54:55 -0700309 // Lock on the app so that we don't try and get the items while apps are being added
310 LauncherAppState app = LauncherAppState.getInstance();
311 LauncherModel model = app.getModel();
312 boolean found = false;
313 synchronized (app) {
Winson Chung64359a52013-07-08 17:17:08 -0700314 if (sWorkerThread.getThreadId() != Process.myTid()) {
315 // Flush the LauncherModel worker thread, so that if we just did another
316 // processInstallShortcut, we give it time for its shortcut to get added to the
317 // database (getItemsInLocalCoordinates reads the database)
318 model.flushWorkerThread();
319 }
Winson Chungc9168342013-06-26 14:54:55 -0700320 final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
Winson Chungc9168342013-06-26 14:54:55 -0700321
322 // Try adding to the workspace screens incrementally, starting at the default or center
323 // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
Winson Chung76828c82013-08-19 15:43:29 -0700324 firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
325 int count = workspaceScreens.size();
Winson Chung156ab5b2013-07-12 14:14:16 -0700326 for (int screen = firstScreenIndex; screen < count && !found; screen++) {
Winson Chungc9168342013-06-26 14:54:55 -0700327 int[] tmpCoordinates = new int[2];
328 if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
Winson Chung76828c82013-08-19 15:43:29 -0700329 workspaceScreens.get(screen))) {
Winson Chungc9168342013-06-26 14:54:55 -0700330 // Update the Launcher db
Winson Chung76828c82013-08-19 15:43:29 -0700331 return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
Winson Chungc9168342013-06-26 14:54:55 -0700332 }
333 }
334 }
Winson Chungc9168342013-06-26 14:54:55 -0700335 return null;
336 }
337
Sunny Goyale755d462014-07-22 13:48:29 -0700338 public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500339 // Process the updated package state
340 Runnable r = new Runnable() {
341 public void run() {
342 Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
343 if (callbacks != null) {
Sunny Goyale755d462014-07-22 13:48:29 -0700344 callbacks.updatePackageState(installInfo);
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500345 }
346 }
347 };
348 mHandler.post(r);
349 }
350
Adam Cohen76a47a12014-02-05 11:47:43 -0800351 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
352 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
353
354 if (allAppsApps == null) {
355 throw new RuntimeException("allAppsApps must not be null");
356 }
357 if (allAppsApps.isEmpty()) {
358 return;
359 }
360
Chris Wrenb6d4c282014-01-27 14:17:08 -0500361 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
362 Iterator<AppInfo> iter = allAppsApps.iterator();
363 while (iter.hasNext()) {
364 ItemInfo a = iter.next();
Sunny Goyal34942622014-08-29 17:20:55 -0700365 if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
Chris Wrenb6d4c282014-01-27 14:17:08 -0500366 restoredAppsFinal.add((AppInfo) a);
367 }
368 }
369
Adam Cohen76a47a12014-02-05 11:47:43 -0800370 // Process the newly added applications and add them to the database first
371 Runnable r = new Runnable() {
372 public void run() {
373 runOnMainThread(new Runnable() {
374 public void run() {
375 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
376 if (callbacks == cb && cb != null) {
Chris Wrenb6d4c282014-01-27 14:17:08 -0500377 if (!restoredAppsFinal.isEmpty()) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500378 for (AppInfo info : restoredAppsFinal) {
379 final Intent intent = info.getIntent();
380 if (intent != null) {
Kenny Guyed131872014-04-30 03:02:21 +0100381 mIconCache.deletePreloadedIcon(intent.getComponent(),
382 info.user);
Chris Wren6d0dde02014-02-10 12:16:54 -0500383 }
384 }
Chris Wrenb6d4c282014-01-27 14:17:08 -0500385 callbacks.bindAppsUpdated(restoredAppsFinal);
386 }
Chris Wren6d0dde02014-02-10 12:16:54 -0500387 callbacks.bindAppsAdded(null, null, null, allAppsApps);
Adam Cohen76a47a12014-02-05 11:47:43 -0800388 }
389 }
390 });
391 }
392 };
393 runOnWorkerThread(r);
Winson Chung997a9232013-07-24 15:33:46 -0700394 }
Adam Cohen76a47a12014-02-05 11:47:43 -0800395
396 public void addAndBindAddedWorkspaceApps(final Context context,
397 final ArrayList<ItemInfo> workspaceApps) {
398 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
399
400 if (workspaceApps == null) {
Winson Chungfe9d96a2013-11-14 11:30:05 -0800401 throw new RuntimeException("workspaceApps and allAppsApps must not be null");
402 }
Adam Cohen76a47a12014-02-05 11:47:43 -0800403 if (workspaceApps.isEmpty()) {
Winson Chung9e6a0a22013-08-27 11:58:12 -0700404 return;
Winson Chung997a9232013-07-24 15:33:46 -0700405 }
Winson Chung64359a52013-07-08 17:17:08 -0700406 // Process the newly added applications and add them to the database first
407 Runnable r = new Runnable() {
408 public void run() {
409 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
410 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
Chris Wrenb6d4c282014-01-27 14:17:08 -0500411 final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>();
Winson Chung64359a52013-07-08 17:17:08 -0700412
Winson Chung76828c82013-08-19 15:43:29 -0700413 // Get the list of workspace screens. We need to append to this list and
414 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
415 // called.
416 ArrayList<Long> workspaceScreens = new ArrayList<Long>();
417 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
418 for (Integer i : orderedScreens.keySet()) {
419 long screenId = orderedScreens.get(i);
420 workspaceScreens.add(screenId);
421 }
422
Winson Chung64359a52013-07-08 17:17:08 -0700423 synchronized(sBgLock) {
Winson Chung94d67682013-09-25 16:29:40 -0700424 Iterator<ItemInfo> iter = workspaceApps.iterator();
Winson Chung64359a52013-07-08 17:17:08 -0700425 while (iter.hasNext()) {
Winson Chung997a9232013-07-24 15:33:46 -0700426 ItemInfo a = iter.next();
Winson Chung64359a52013-07-08 17:17:08 -0700427 final String name = a.title.toString();
Winson Chung997a9232013-07-24 15:33:46 -0700428 final Intent launchIntent = a.getIntent();
Winson Chung64359a52013-07-08 17:17:08 -0700429
430 // Short-circuit this logic if the icon exists somewhere on the workspace
431 if (LauncherModel.shortcutExists(context, name, launchIntent)) {
Chris Wrenb6d4c282014-01-27 14:17:08 -0500432 // Only InstallShortcutReceiver sends us shortcutInfos, ignore them
433 if (a instanceof AppInfo &&
Sunny Goyal34942622014-08-29 17:20:55 -0700434 LauncherModel.appWasPromise(context, launchIntent, a.user)) {
Chris Wrenb6d4c282014-01-27 14:17:08 -0500435 restoredAppsFinal.add((AppInfo) a);
436 }
Winson Chung64359a52013-07-08 17:17:08 -0700437 continue;
438 }
439
Winson Chung87412982013-10-03 18:34:14 -0700440 // Add this icon to the db, creating a new page if necessary. If there
441 // is only the empty page then we just add items to the first page.
442 // Otherwise, we add them to the next pages.
443 int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
Winson Chung64359a52013-07-08 17:17:08 -0700444 Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
Winson Chung76828c82013-08-19 15:43:29 -0700445 name, launchIntent, startSearchPageIndex, workspaceScreens);
Winson Chung64359a52013-07-08 17:17:08 -0700446 if (coords == null) {
Michael Jurka414300a2013-08-27 15:42:35 +0200447 LauncherProvider lp = LauncherAppState.getLauncherProvider();
Winson Chungc763c4e2013-07-19 13:49:06 -0700448
449 // If we can't find a valid position, then just add a new screen.
450 // This takes time so we need to re-queue the add until the new
451 // page is added. Create as many screens as necessary to satisfy
452 // the startSearchPageIndex.
453 int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
Winson Chung76828c82013-08-19 15:43:29 -0700454 workspaceScreens.size());
Winson Chungc763c4e2013-07-19 13:49:06 -0700455 while (numPagesToAdd > 0) {
456 long screenId = lp.generateNewScreenId();
Winson Chungc763c4e2013-07-19 13:49:06 -0700457 // Save the screen id for binding in the workspace
Winson Chung76828c82013-08-19 15:43:29 -0700458 workspaceScreens.add(screenId);
Winson Chungc763c4e2013-07-19 13:49:06 -0700459 addedWorkspaceScreensFinal.add(screenId);
460 numPagesToAdd--;
461 }
Winson Chung76828c82013-08-19 15:43:29 -0700462
Winson Chung64359a52013-07-08 17:17:08 -0700463 // Find the coordinate again
464 coords = LauncherModel.findNextAvailableIconSpace(context,
Winson Chung76828c82013-08-19 15:43:29 -0700465 name, launchIntent, startSearchPageIndex, workspaceScreens);
Winson Chung64359a52013-07-08 17:17:08 -0700466 }
467 if (coords == null) {
468 throw new RuntimeException("Coordinates should not be null");
469 }
470
Winson Chung997a9232013-07-24 15:33:46 -0700471 ShortcutInfo shortcutInfo;
472 if (a instanceof ShortcutInfo) {
473 shortcutInfo = (ShortcutInfo) a;
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200474 } else if (a instanceof AppInfo) {
475 shortcutInfo = ((AppInfo) a).makeShortcut();
Winson Chung997a9232013-07-24 15:33:46 -0700476 } else {
477 throw new RuntimeException("Unexpected info type");
478 }
Winson Chung94d67682013-09-25 16:29:40 -0700479
Winson Chung64359a52013-07-08 17:17:08 -0700480 // Add the shortcut to the db
481 addItemToDatabase(context, shortcutInfo,
482 LauncherSettings.Favorites.CONTAINER_DESKTOP,
483 coords.first, coords.second[0], coords.second[1], false);
484 // Save the ShortcutInfo for binding in the workspace
485 addedShortcutsFinal.add(shortcutInfo);
486 }
487 }
488
Winson Chung76828c82013-08-19 15:43:29 -0700489 // Update the workspace screens
490 updateWorkspaceScreenOrder(context, workspaceScreens);
491
Adam Cohen76a47a12014-02-05 11:47:43 -0800492 if (!addedShortcutsFinal.isEmpty()) {
Winson Chung997a9232013-07-24 15:33:46 -0700493 runOnMainThread(new Runnable() {
494 public void run() {
495 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
496 if (callbacks == cb && cb != null) {
Winson Chung997a9232013-07-24 15:33:46 -0700497 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
498 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
Winson Chung94d67682013-09-25 16:29:40 -0700499 if (!addedShortcutsFinal.isEmpty()) {
500 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
501 long lastScreenId = info.screenId;
502 for (ItemInfo i : addedShortcutsFinal) {
503 if (i.screenId == lastScreenId) {
504 addAnimated.add(i);
505 } else {
506 addNotAnimated.add(i);
507 }
Winson Chung997a9232013-07-24 15:33:46 -0700508 }
509 }
Winson Chungd64d1762013-08-20 14:37:16 -0700510 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
Adam Cohen76a47a12014-02-05 11:47:43 -0800511 addNotAnimated, addAnimated, null);
Chris Wrenb6d4c282014-01-27 14:17:08 -0500512 if (!restoredAppsFinal.isEmpty()) {
513 callbacks.bindAppsUpdated(restoredAppsFinal);
514 }
Winson Chung997a9232013-07-24 15:33:46 -0700515 }
Winson Chung64359a52013-07-08 17:17:08 -0700516 }
Winson Chung997a9232013-07-24 15:33:46 -0700517 });
518 }
Winson Chung64359a52013-07-08 17:17:08 -0700519 }
520 };
521 runOnWorkerThread(r);
522 }
523
Winson Chung81b52252012-08-27 15:34:29 -0700524 public void unbindItemInfosAndClearQueuedBindRunnables() {
525 if (sWorkerThread.getThreadId() == Process.myTid()) {
526 throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
527 "main thread");
528 }
529
530 // Clear any deferred bind runnables
Jason Monka0a7a742014-04-22 09:23:19 -0400531 synchronized (mDeferredBindRunnables) {
532 mDeferredBindRunnables.clear();
533 }
Winson Chung81b52252012-08-27 15:34:29 -0700534 // Remove any queued bind runnables
535 mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
536 // Unbind all the workspace items
537 unbindWorkspaceItemsOnMainThread();
Winson Chung603bcb92011-09-02 11:45:39 -0700538 }
539
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700540 /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
Winson Chung81b52252012-08-27 15:34:29 -0700541 void unbindWorkspaceItemsOnMainThread() {
Winson Chung603bcb92011-09-02 11:45:39 -0700542 // Ensure that we don't use the same workspace items data structure on the main thread
543 // by making a copy of workspace items first.
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700544 final ArrayList<ItemInfo> tmpWorkspaceItems = new ArrayList<ItemInfo>();
545 final ArrayList<ItemInfo> tmpAppWidgets = new ArrayList<ItemInfo>();
Winson Chung2abf94d2012-07-18 18:16:38 -0700546 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700547 tmpWorkspaceItems.addAll(sBgWorkspaceItems);
548 tmpAppWidgets.addAll(sBgAppWidgets);
549 }
550 Runnable r = new Runnable() {
551 @Override
552 public void run() {
553 for (ItemInfo item : tmpWorkspaceItems) {
554 item.unbind();
555 }
556 for (ItemInfo item : tmpAppWidgets) {
557 item.unbind();
558 }
559 }
560 };
561 runOnMainThread(r);
Adam Cohen4eac29a2011-07-11 17:53:37 -0700562 }
563
Joe Onorato9c1289c2009-08-17 11:03:03 -0400564 /**
565 * Adds an item to the DB if it was not created previously, or move it to a new
566 * <container, screen, cellX, cellY>
567 */
568 static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700569 long screenId, int cellX, int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400570 if (item.container == ItemInfo.NO_ID) {
571 // From all apps
Adam Cohendcd297f2013-06-18 13:13:40 -0700572 addItemToDatabase(context, item, container, screenId, cellX, cellY, false);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400573 } else {
574 // From somewhere else
Adam Cohendcd297f2013-06-18 13:13:40 -0700575 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800576 }
577 }
578
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700579 static void checkItemInfoLocked(
580 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
581 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
582 if (modelItem != null && item != modelItem) {
583 // check all the data is consistent
584 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
585 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
586 ShortcutInfo shortcut = (ShortcutInfo) item;
587 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
588 modelShortcut.intent.filterEquals(shortcut.intent) &&
589 modelShortcut.id == shortcut.id &&
590 modelShortcut.itemType == shortcut.itemType &&
591 modelShortcut.container == shortcut.container &&
Adam Cohendcd297f2013-06-18 13:13:40 -0700592 modelShortcut.screenId == shortcut.screenId &&
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700593 modelShortcut.cellX == shortcut.cellX &&
594 modelShortcut.cellY == shortcut.cellY &&
595 modelShortcut.spanX == shortcut.spanX &&
596 modelShortcut.spanY == shortcut.spanY &&
597 ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
598 (modelShortcut.dropPos != null &&
599 shortcut.dropPos != null &&
600 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
601 modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
602 // For all intents and purposes, this is the same object
603 return;
604 }
605 }
606
607 // the modelItem needs to match up perfectly with item if our model is
608 // to be consistent with the database-- for now, just require
609 // modelItem == item or the equality check above
610 String msg = "item: " + ((item != null) ? item.toString() : "null") +
611 "modelItem: " +
612 ((modelItem != null) ? modelItem.toString() : "null") +
613 "Error: ItemInfo passed to checkItemInfo doesn't match original";
614 RuntimeException e = new RuntimeException(msg);
615 if (stackTrace != null) {
616 e.setStackTrace(stackTrace);
617 }
Adam Cohenb9ada652013-11-08 08:25:08 -0800618 throw e;
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700619 }
620 }
621
Michael Jurka816474f2012-06-25 14:49:02 -0700622 static void checkItemInfo(final ItemInfo item) {
623 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
624 final long itemId = item.id;
625 Runnable r = new Runnable() {
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700626 public void run() {
627 synchronized (sBgLock) {
628 checkItemInfoLocked(itemId, item, stackTrace);
Michael Jurka816474f2012-06-25 14:49:02 -0700629 }
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700630 }
631 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700632 runOnWorkerThread(r);
Michael Jurka816474f2012-06-25 14:49:02 -0700633 }
634
Michael Jurkac9d95c52011-08-29 14:03:34 -0700635 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
636 final ItemInfo item, final String callingFunction) {
637 final long itemId = item.id;
638 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
639 final ContentResolver cr = context.getContentResolver();
640
Adam Cohen487f7dd2012-06-28 18:12:10 -0700641 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700642 Runnable r = new Runnable() {
643 public void run() {
644 cr.update(uri, values, null, null);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700645 updateItemArrays(item, itemId, stackTrace);
646 }
647 };
648 runOnWorkerThread(r);
649 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700650
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700651 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
652 final ArrayList<ItemInfo> items, final String callingFunction) {
653 final ContentResolver cr = context.getContentResolver();
Adam Cohen487f7dd2012-06-28 18:12:10 -0700654
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700655 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
656 Runnable r = new Runnable() {
657 public void run() {
658 ArrayList<ContentProviderOperation> ops =
659 new ArrayList<ContentProviderOperation>();
660 int count = items.size();
661 for (int i = 0; i < count; i++) {
662 ItemInfo item = items.get(i);
663 final long itemId = item.id;
664 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
665 ContentValues values = valuesList.get(i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700666
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700667 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
668 updateItemArrays(item, itemId, stackTrace);
669
670 }
671 try {
672 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
673 } catch (Exception e) {
674 e.printStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700675 }
676 }
677 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700678 runOnWorkerThread(r);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700679 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700680
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700681 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
682 // Lock on mBgLock *after* the db operation
683 synchronized (sBgLock) {
684 checkItemInfoLocked(itemId, item, stackTrace);
685
686 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
687 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
688 // Item is in a folder, make sure this folder exists
689 if (!sBgFolders.containsKey(item.container)) {
690 // An items container is being set to a that of an item which is not in
691 // the list of Folders.
692 String msg = "item: " + item + " container being set to: " +
693 item.container + ", not in the list of folders";
694 Log.e(TAG, msg);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700695 }
696 }
697
698 // Items are added/removed from the corresponding FolderInfo elsewhere, such
699 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
700 // that are on the desktop, as appropriate
701 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
Winson Chung33231f52013-12-09 16:57:45 -0800702 if (modelItem != null &&
703 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
704 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700705 switch (modelItem.itemType) {
706 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
707 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
708 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
709 if (!sBgWorkspaceItems.contains(modelItem)) {
710 sBgWorkspaceItems.add(modelItem);
711 }
712 break;
713 default:
714 break;
715 }
716 } else {
717 sBgWorkspaceItems.remove(modelItem);
718 }
719 }
720 }
721
Michael Jurkac7700af2013-05-14 20:17:58 +0200722 public void flushWorkerThread() {
723 mFlushingWorkerThread = true;
724 Runnable waiter = new Runnable() {
725 public void run() {
726 synchronized (this) {
727 notifyAll();
728 mFlushingWorkerThread = false;
729 }
730 }
731 };
732
733 synchronized(waiter) {
734 runOnWorkerThread(waiter);
735 if (mLoaderTask != null) {
736 synchronized(mLoaderTask) {
737 mLoaderTask.notify();
738 }
739 }
740 boolean success = false;
741 while (!success) {
742 try {
743 waiter.wait();
744 success = true;
745 } catch (InterruptedException e) {
746 }
747 }
748 }
749 }
750
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800751 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400752 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700753 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700754 static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700755 final long screenId, final int cellX, final int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400756 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400757 item.cellX = cellX;
758 item.cellY = cellY;
Michael Jurkac9d95c52011-08-29 14:03:34 -0700759
Winson Chung3d503fb2011-07-13 17:25:49 -0700760 // We store hotseat items in canonical form which is this orientation invariant position
761 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700762 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -0700763 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700764 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -0700765 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700766 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -0700767 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400768
769 final ContentValues values = new ContentValues();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400770 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Winson Chung3d503fb2011-07-13 17:25:49 -0700771 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
772 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700773 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400774
Michael Jurkac9d95c52011-08-29 14:03:34 -0700775 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700776 }
777
778 /**
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700779 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
780 * cellX, cellY have already been updated on the ItemInfos.
781 */
782 static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
783 final long container, final int screen) {
784
785 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
786 int count = items.size();
787
788 for (int i = 0; i < count; i++) {
789 ItemInfo item = items.get(i);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700790 item.container = container;
791
792 // We store hotseat items in canonical form which is this orientation invariant position
793 // in the hotseat
794 if (context instanceof Launcher && screen < 0 &&
795 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700796 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700797 item.cellY);
798 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700799 item.screenId = screen;
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700800 }
801
802 final ContentValues values = new ContentValues();
803 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
804 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
805 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700806 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700807
808 contentValues.add(values);
809 }
810 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
811 }
812
813 /**
Adam Cohenbebf0422012-04-11 18:06:28 -0700814 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
Adam Cohend4844c32011-02-18 19:25:06 -0800815 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700816 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700817 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
Winson Chung0f84a602013-09-30 14:30:58 -0700818 item.container = container;
Adam Cohend4844c32011-02-18 19:25:06 -0800819 item.cellX = cellX;
820 item.cellY = cellY;
Adam Cohenbebf0422012-04-11 18:06:28 -0700821 item.spanX = spanX;
822 item.spanY = spanY;
823
824 // We store hotseat items in canonical form which is this orientation invariant position
825 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700826 if (context instanceof Launcher && screenId < 0 &&
Adam Cohenbebf0422012-04-11 18:06:28 -0700827 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700828 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Adam Cohenbebf0422012-04-11 18:06:28 -0700829 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700830 item.screenId = screenId;
Adam Cohenbebf0422012-04-11 18:06:28 -0700831 }
Adam Cohend4844c32011-02-18 19:25:06 -0800832
Adam Cohend4844c32011-02-18 19:25:06 -0800833 final ContentValues values = new ContentValues();
Adam Cohend4844c32011-02-18 19:25:06 -0800834 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Adam Cohenbebf0422012-04-11 18:06:28 -0700835 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
836 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
837 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
838 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700839 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohend4844c32011-02-18 19:25:06 -0800840
Michael Jurka816474f2012-06-25 14:49:02 -0700841 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
Adam Cohenbebf0422012-04-11 18:06:28 -0700842 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700843
844 /**
845 * Update an item to the database in a specified container.
846 */
847 static void updateItemInDatabase(Context context, final ItemInfo item) {
848 final ContentValues values = new ContentValues();
Kenny Guyed131872014-04-30 03:02:21 +0100849 item.onAddToDatabase(context, values);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700850 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
851 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
Adam Cohend4844c32011-02-18 19:25:06 -0800852 }
853
854 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400855 * Returns true if the shortcuts already exists in the database.
856 * we identify a shortcut by its title and intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800857 */
Joe Onorato9c1289c2009-08-17 11:03:03 -0400858 static boolean shortcutExists(Context context, String title, Intent intent) {
859 final ContentResolver cr = context.getContentResolver();
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700860 final Intent intentWithPkg, intentWithoutPkg;
861
862 if (intent.getComponent() != null) {
863 // If component is not null, an intent with null package will produce
864 // the same result and should also be a match.
865 if (intent.getPackage() != null) {
866 intentWithPkg = intent;
867 intentWithoutPkg = new Intent(intent).setPackage(null);
868 } else {
869 intentWithPkg = new Intent(intent).setPackage(
870 intent.getComponent().getPackageName());
871 intentWithoutPkg = intent;
872 }
873 } else {
874 intentWithPkg = intent;
875 intentWithoutPkg = intent;
876 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400877 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700878 new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
879 new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400880 boolean result = false;
881 try {
882 result = c.moveToFirst();
883 } finally {
884 c.close();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800885 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400886 return result;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700887 }
888
Joe Onorato9c1289c2009-08-17 11:03:03 -0400889 /**
Sunny Goyal34942622014-08-29 17:20:55 -0700890 * Returns true if the promise shortcuts with the same package name exists on the workspace.
Chris Wrenb6d4c282014-01-27 14:17:08 -0500891 */
Sunny Goyal34942622014-08-29 17:20:55 -0700892 static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
Chris Wrenb6d4c282014-01-27 14:17:08 -0500893 final ComponentName component = intent.getComponent();
894 if (component == null) {
895 return false;
896 }
Sunny Goyal34942622014-08-29 17:20:55 -0700897 return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
Chris Wrenb6d4c282014-01-27 14:17:08 -0500898 }
899
900 /**
Winson Chungaafa03c2010-06-11 17:34:16 -0700901 * Returns an ItemInfo array containing all the items in the LauncherModel.
902 * The ItemInfo.id is not set through this function.
903 */
904 static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
905 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
906 final ContentResolver cr = context.getContentResolver();
907 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
908 LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
Kenny Guyed131872014-04-30 03:02:21 +0100909 LauncherSettings.Favorites.SCREEN,
910 LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
911 LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
912 LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
Winson Chungaafa03c2010-06-11 17:34:16 -0700913
914 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
915 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
916 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
917 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
918 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
919 final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
920 final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
Kenny Guyed131872014-04-30 03:02:21 +0100921 final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
922 UserManagerCompat userManager = UserManagerCompat.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700923 try {
924 while (c.moveToNext()) {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700925 ItemInfo item = new ItemInfo();
Winson Chungaafa03c2010-06-11 17:34:16 -0700926 item.cellX = c.getInt(cellXIndex);
927 item.cellY = c.getInt(cellYIndex);
Winson Chung61c69862013-08-21 19:10:29 -0700928 item.spanX = Math.max(1, c.getInt(spanXIndex));
929 item.spanY = Math.max(1, c.getInt(spanYIndex));
Winson Chungaafa03c2010-06-11 17:34:16 -0700930 item.container = c.getInt(containerIndex);
931 item.itemType = c.getInt(itemTypeIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700932 item.screenId = c.getInt(screenIndex);
Kenny Guy1317e2d2014-05-08 18:52:50 +0100933 long serialNumber = c.getInt(profileIdIndex);
Kenny Guyed131872014-04-30 03:02:21 +0100934 item.user = userManager.getUserForSerialNumber(serialNumber);
935 // Skip if user has been deleted.
936 if (item.user != null) {
937 items.add(item);
938 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700939 }
940 } catch (Exception e) {
941 items.clear();
942 } finally {
943 c.close();
944 }
945
946 return items;
947 }
948
949 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400950 * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
951 */
952 FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
953 final ContentResolver cr = context.getContentResolver();
954 Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
955 "_id=? and (itemType=? or itemType=?)",
956 new String[] { String.valueOf(id),
Adam Cohendf2cc412011-04-27 16:56:57 -0700957 String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700958
Joe Onorato9c1289c2009-08-17 11:03:03 -0400959 try {
960 if (c.moveToFirst()) {
961 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
962 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
963 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
964 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
965 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
966 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800967
Joe Onorato9c1289c2009-08-17 11:03:03 -0400968 FolderInfo folderInfo = null;
969 switch (c.getInt(itemTypeIndex)) {
Adam Cohendf2cc412011-04-27 16:56:57 -0700970 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
971 folderInfo = findOrMakeFolder(folderList, id);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400972 break;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700973 }
974
Joe Onorato9c1289c2009-08-17 11:03:03 -0400975 folderInfo.title = c.getString(titleIndex);
976 folderInfo.id = id;
977 folderInfo.container = c.getInt(containerIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700978 folderInfo.screenId = c.getInt(screenIndex);
Adam Cohend22015c2010-07-26 22:02:18 -0700979 folderInfo.cellX = c.getInt(cellXIndex);
980 folderInfo.cellY = c.getInt(cellYIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400981
982 return folderInfo;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700983 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400984 } finally {
985 c.close();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700986 }
987
988 return null;
989 }
990
Joe Onorato9c1289c2009-08-17 11:03:03 -0400991 /**
992 * Add an item to the database in a specified container. Sets the container, screen, cellX and
993 * cellY fields of the item. Also assigns an ID to the item.
994 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700995 static void addItemToDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700996 final long screenId, final int cellX, final int cellY, final boolean notify) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400997 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400998 item.cellX = cellX;
999 item.cellY = cellY;
Winson Chung3d503fb2011-07-13 17:25:49 -07001000 // We store hotseat items in canonical form which is this orientation invariant position
1001 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -07001002 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -07001003 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001004 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -07001005 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07001006 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -07001007 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001008
1009 final ContentValues values = new ContentValues();
1010 final ContentResolver cr = context.getContentResolver();
Kenny Guyed131872014-04-30 03:02:21 +01001011 item.onAddToDatabase(context, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001012
Michael Jurka414300a2013-08-27 15:42:35 +02001013 item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001014 values.put(LauncherSettings.Favorites._ID, item.id);
Winson Chung3d503fb2011-07-13 17:25:49 -07001015 item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
Winson Chungaafa03c2010-06-11 17:34:16 -07001016
Jason Monk8e19cf22014-03-20 15:06:57 -04001017 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -07001018 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001019 public void run() {
1020 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
1021 LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001022
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001023 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001024 synchronized (sBgLock) {
Jason Monk8e19cf22014-03-20 15:06:57 -04001025 checkItemInfoLocked(item.id, item, stackTrace);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001026 sBgItemsIdMap.put(item.id, item);
1027 switch (item.itemType) {
1028 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1029 sBgFolders.put(item.id, (FolderInfo) item);
1030 // Fall through
1031 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1032 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1033 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
1034 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1035 sBgWorkspaceItems.add(item);
1036 } else {
1037 if (!sBgFolders.containsKey(item.container)) {
1038 // Adding an item to a folder that doesn't exist.
1039 String msg = "adding item: " + item + " to a folder that " +
1040 " doesn't exist";
Adam Cohen28b3e102012-10-04 17:21:33 -07001041 Log.e(TAG, msg);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001042 }
Adam Cohen487f7dd2012-06-28 18:12:10 -07001043 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001044 break;
1045 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1046 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
1047 break;
1048 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001049 }
1050 }
Michael Jurkac9d95c52011-08-29 14:03:34 -07001051 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001052 runOnWorkerThread(r);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001053 }
1054
Joe Onorato9c1289c2009-08-17 11:03:03 -04001055 /**
Winson Chungaafa03c2010-06-11 17:34:16 -07001056 * Creates a new unique child id, for a given cell span across all layouts.
1057 */
Michael Jurka845ba3b2010-09-28 17:09:46 -07001058 static int getCellLayoutChildId(
Adam Cohendcd297f2013-06-18 13:13:40 -07001059 long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001060 return (((int) container & 0xFF) << 24)
Adam Cohendcd297f2013-06-18 13:13:40 -07001061 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
Winson Chungaafa03c2010-06-11 17:34:16 -07001062 }
1063
Sunny Goyal34942622014-08-29 17:20:55 -07001064 private static ArrayList<ItemInfo> getItemsByPackageName(
1065 final String pn, final UserHandleCompat user) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001066 ItemInfoFilter filter = new ItemInfoFilter() {
1067 @Override
1068 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
1069 return cn.getPackageName().equals(pn) && info.user.equals(user);
1070 }
1071 };
Sunny Goyal34942622014-08-29 17:20:55 -07001072 return filterItemInfos(sBgItemsIdMap.values(), filter);
1073 }
1074
1075 /**
1076 * Removes all the items from the database corresponding to the specified package.
1077 */
1078 static void deletePackageFromDatabase(Context context, final String pn,
1079 final UserHandleCompat user) {
1080 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001081 }
1082
1083 /**
Michael Jurkac9d95c52011-08-29 14:03:34 -07001084 * Removes the specified item from the database
1085 * @param context
1086 * @param item
Joe Onorato9c1289c2009-08-17 11:03:03 -04001087 */
Michael Jurkac9d95c52011-08-29 14:03:34 -07001088 static void deleteItemFromDatabase(Context context, final ItemInfo item) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001089 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
1090 items.add(item);
1091 deleteItemsFromDatabase(context, items);
1092 }
1093
1094 /**
1095 * Removes the specified items from the database
1096 * @param context
1097 * @param item
1098 */
1099 static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001100 final ContentResolver cr = context.getContentResolver();
Adam Cohen487f7dd2012-06-28 18:12:10 -07001101
Michael Jurka83df1882011-08-31 20:59:26 -07001102 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001103 public void run() {
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001104 for (ItemInfo item : items) {
1105 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
1106 cr.delete(uri, null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001107
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001108 // Lock on mBgLock *after* the db operation
1109 synchronized (sBgLock) {
1110 switch (item.itemType) {
1111 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1112 sBgFolders.remove(item.id);
1113 for (ItemInfo info: sBgItemsIdMap.values()) {
1114 if (info.container == item.id) {
1115 // We are deleting a folder which still contains items that
1116 // think they are contained by that folder.
1117 String msg = "deleting a folder (" + item + ") which still " +
1118 "contains items (" + info + ")";
1119 Log.e(TAG, msg);
1120 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001121 }
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001122 sBgWorkspaceItems.remove(item);
1123 break;
1124 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1125 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1126 sBgWorkspaceItems.remove(item);
1127 break;
1128 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1129 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1130 break;
1131 }
1132 sBgItemsIdMap.remove(item.id);
1133 sBgDbIconCache.remove(item);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001134 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001135 }
1136 }
Michael Jurka83df1882011-08-31 20:59:26 -07001137 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001138 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001139 }
1140
1141 /**
Adam Cohendcd297f2013-06-18 13:13:40 -07001142 * Update the order of the workspace screens in the database. The array list contains
1143 * a list of screen ids in the order that they should appear.
1144 */
Winson Chungc9168342013-06-26 14:54:55 -07001145 void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
Winson Chunga90303b2013-11-15 13:05:06 -08001146 // Log to disk
1147 Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
1148 Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
1149
Winson Chung64359a52013-07-08 17:17:08 -07001150 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
Adam Cohendcd297f2013-06-18 13:13:40 -07001151 final ContentResolver cr = context.getContentResolver();
1152 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1153
1154 // Remove any negative screen ids -- these aren't persisted
Winson Chung64359a52013-07-08 17:17:08 -07001155 Iterator<Long> iter = screensCopy.iterator();
Adam Cohendcd297f2013-06-18 13:13:40 -07001156 while (iter.hasNext()) {
1157 long id = iter.next();
1158 if (id < 0) {
1159 iter.remove();
1160 }
1161 }
1162
1163 Runnable r = new Runnable() {
1164 @Override
1165 public void run() {
Yura085c8532014-02-11 15:15:29 +00001166 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
Adam Cohendcd297f2013-06-18 13:13:40 -07001167 // Clear the table
Yura085c8532014-02-11 15:15:29 +00001168 ops.add(ContentProviderOperation.newDelete(uri).build());
Winson Chung76828c82013-08-19 15:43:29 -07001169 int count = screensCopy.size();
Adam Cohendcd297f2013-06-18 13:13:40 -07001170 for (int i = 0; i < count; i++) {
1171 ContentValues v = new ContentValues();
Winson Chung76828c82013-08-19 15:43:29 -07001172 long screenId = screensCopy.get(i);
Adam Cohendcd297f2013-06-18 13:13:40 -07001173 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1174 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
Yura085c8532014-02-11 15:15:29 +00001175 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
Adam Cohendcd297f2013-06-18 13:13:40 -07001176 }
Yura085c8532014-02-11 15:15:29 +00001177
1178 try {
1179 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1180 } catch (Exception ex) {
1181 throw new RuntimeException(ex);
1182 }
Winson Chung9e6a0a22013-08-27 11:58:12 -07001183
Winson Chungba9c37f2013-08-30 14:11:37 -07001184 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001185 sBgWorkspaceScreens.clear();
1186 sBgWorkspaceScreens.addAll(screensCopy);
Adam Cohen4caf2982013-08-20 18:54:31 -07001187 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001188 }
1189 };
1190 runOnWorkerThread(r);
1191 }
1192
1193 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001194 * Remove the contents of the specified folder from the database
1195 */
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001196 static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001197 final ContentResolver cr = context.getContentResolver();
1198
Michael Jurkac9d95c52011-08-29 14:03:34 -07001199 Runnable r = new Runnable() {
1200 public void run() {
1201 cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001202 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001203 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001204 sBgItemsIdMap.remove(info.id);
1205 sBgFolders.remove(info.id);
1206 sBgDbIconCache.remove(info);
1207 sBgWorkspaceItems.remove(info);
1208 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001209
Michael Jurkac9d95c52011-08-29 14:03:34 -07001210 cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
1211 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001212 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001213 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001214 for (ItemInfo childInfo : info.contents) {
1215 sBgItemsIdMap.remove(childInfo.id);
1216 sBgDbIconCache.remove(childInfo);
1217 }
Adam Cohenafb01ee2011-06-23 15:38:03 -07001218 }
Michael Jurkac9d95c52011-08-29 14:03:34 -07001219 }
1220 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001221 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001222 }
1223
1224 /**
1225 * Set this as the current Launcher activity object for the loader.
1226 */
1227 public void initialize(Callbacks callbacks) {
1228 synchronized (mLock) {
1229 mCallbacks = new WeakReference<Callbacks>(callbacks);
1230 }
1231 }
1232
Kenny Guyed131872014-04-30 03:02:21 +01001233 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001234 public void onPackageChanged(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001235 int op = PackageUpdatedTask.OP_UPDATE;
1236 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1237 user));
1238 }
1239
1240 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001241 public void onPackageRemoved(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001242 int op = PackageUpdatedTask.OP_REMOVE;
1243 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1244 user));
1245 }
1246
1247 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001248 public void onPackageAdded(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001249 int op = PackageUpdatedTask.OP_ADD;
1250 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1251 user));
1252 }
1253
1254 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001255 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
Kenny Guyed131872014-04-30 03:02:21 +01001256 boolean replacing) {
1257 if (!replacing) {
1258 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
1259 user));
1260 if (mAppsCanBeOnRemoveableStorage) {
1261 // Only rebind if we support removable storage. It catches the
1262 // case where
1263 // apps on the external sd card need to be reloaded
1264 startLoaderFromBackground();
1265 }
1266 } else {
1267 // If we are replacing then just update the packages in the list
1268 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
1269 packageNames, user));
1270 }
1271 }
1272
1273 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001274 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
Kenny Guyed131872014-04-30 03:02:21 +01001275 boolean replacing) {
1276 if (!replacing) {
1277 enqueuePackageUpdated(new PackageUpdatedTask(
1278 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1279 user));
1280 }
1281
1282 }
1283
Joe Onorato1d8e7bb2009-10-15 19:49:43 -07001284 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001285 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1286 * ACTION_PACKAGE_CHANGED.
1287 */
Narayan Kamathcb1a4772011-06-28 13:46:59 +01001288 @Override
Joe Onoratof99f8c12009-10-31 17:27:36 -04001289 public void onReceive(Context context, Intent intent) {
Chris Wrenb358f812014-04-16 13:37:00 -04001290 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
Winson Chungaafa03c2010-06-11 17:34:16 -07001291
Joe Onorato36115782010-06-17 13:28:48 -04001292 final String action = intent.getAction();
Kenny Guyed131872014-04-30 03:02:21 +01001293 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Reena Lee93f824a2011-09-23 17:20:28 -07001294 // If we have changed locale we need to clear out the labels in all apps/workspace.
1295 forceReload();
1296 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1297 // Check if configuration change was an mcc/mnc change which would affect app resources
1298 // and we would need to clear out the labels in all apps/workspace. Same handling as
1299 // above for ACTION_LOCALE_CHANGED
1300 Configuration currentConfig = context.getResources().getConfiguration();
Reena Lee99a73f32011-10-24 17:27:37 -07001301 if (mPreviousConfigMcc != currentConfig.mcc) {
Reena Lee93f824a2011-09-23 17:20:28 -07001302 Log.d(TAG, "Reload apps on config change. curr_mcc:"
Reena Lee99a73f32011-10-24 17:27:37 -07001303 + currentConfig.mcc + " prevmcc:" + mPreviousConfigMcc);
Reena Lee93f824a2011-09-23 17:20:28 -07001304 forceReload();
1305 }
1306 // Update previousConfig
Reena Lee99a73f32011-10-24 17:27:37 -07001307 mPreviousConfigMcc = currentConfig.mcc;
Winson Chungcbf7c4d2011-08-23 11:58:54 -07001308 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
1309 SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
Michael Jurkaec9788e2011-08-29 11:24:45 -07001310 if (mCallbacks != null) {
1311 Callbacks callbacks = mCallbacks.get();
1312 if (callbacks != null) {
1313 callbacks.bindSearchablesChanged();
1314 }
Winson Chungcfdf7ee2011-08-25 11:38:34 -07001315 }
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001316 }
1317 }
1318
Amith Yamasani6cc806d2014-05-02 13:47:11 -07001319 void forceReload() {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001320 resetLoadedState(true, true);
1321
Reena Lee93f824a2011-09-23 17:20:28 -07001322 // Do this here because if the launcher activity is running it will be restarted.
1323 // If it's not running startLoaderFromBackground will merely tell it that it needs
1324 // to reload.
1325 startLoaderFromBackground();
1326 }
1327
Winson Chungf0c6ae02012-03-21 16:10:31 -07001328 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1329 synchronized (mLock) {
1330 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1331 // mWorkspaceLoaded to true later
1332 stopLoaderLocked();
1333 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1334 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1335 }
1336 }
1337
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001338 /**
1339 * When the launcher is in the background, it's possible for it to miss paired
1340 * configuration changes. So whenever we trigger the loader from the background
1341 * tell the launcher that it needs to re-run the loader when it comes back instead
1342 * of doing it now.
1343 */
1344 public void startLoaderFromBackground() {
1345 boolean runLoader = false;
1346 if (mCallbacks != null) {
1347 Callbacks callbacks = mCallbacks.get();
1348 if (callbacks != null) {
1349 // Only actually run the loader if they're not paused.
1350 if (!callbacks.setLoadOnResume()) {
1351 runLoader = true;
1352 }
1353 }
1354 }
1355 if (runLoader) {
Derek Prothro7aff3992013-12-10 14:00:37 -05001356 startLoader(false, PagedView.INVALID_RESTORE_PAGE);
Joe Onorato790c2d92010-06-11 00:14:11 -07001357 }
Joe Onorato36115782010-06-17 13:28:48 -04001358 }
Joe Onoratof99f8c12009-10-31 17:27:36 -04001359
Reena Lee93f824a2011-09-23 17:20:28 -07001360 // If there is already a loader task running, tell it to stop.
1361 // returns true if isLaunching() was true on the old task
1362 private boolean stopLoaderLocked() {
1363 boolean isLaunching = false;
1364 LoaderTask oldTask = mLoaderTask;
1365 if (oldTask != null) {
1366 if (oldTask.isLaunching()) {
1367 isLaunching = true;
1368 }
1369 oldTask.stopLocked();
1370 }
1371 return isLaunching;
1372 }
1373
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001374 public void startLoader(boolean isLaunching, int synchronousBindPage) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001375 startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
1376 }
1377
1378 public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
Joe Onorato36115782010-06-17 13:28:48 -04001379 synchronized (mLock) {
1380 if (DEBUG_LOADERS) {
1381 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
1382 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001383
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001384 // Clear any deferred bind-runnables from the synchronized load process
1385 // We must do this before any loading/binding is scheduled below.
Jason Monka0a7a742014-04-22 09:23:19 -04001386 synchronized (mDeferredBindRunnables) {
1387 mDeferredBindRunnables.clear();
1388 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001389
Joe Onorato36115782010-06-17 13:28:48 -04001390 // Don't bother to start the thread if we know it's not going to do anything
1391 if (mCallbacks != null && mCallbacks.get() != null) {
1392 // If there is already one running, tell it to stop.
Reena Lee93f824a2011-09-23 17:20:28 -07001393 // also, don't downgrade isLaunching if we're already running
1394 isLaunching = isLaunching || stopLoaderLocked();
Dan Sandlerd5024042014-01-09 15:01:33 -05001395 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
Derek Prothro7aff3992013-12-10 14:00:37 -05001396 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
1397 && mAllAppsLoaded && mWorkspaceLoaded) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001398 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1399 } else {
1400 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1401 sWorker.post(mLoaderTask);
1402 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001403 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001404 }
1405 }
1406
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001407 void bindRemainingSynchronousPages() {
1408 // Post the remaining side pages to be loaded
1409 if (!mDeferredBindRunnables.isEmpty()) {
Jason Monka0a7a742014-04-22 09:23:19 -04001410 Runnable[] deferredBindRunnables = null;
1411 synchronized (mDeferredBindRunnables) {
1412 deferredBindRunnables = mDeferredBindRunnables.toArray(
1413 new Runnable[mDeferredBindRunnables.size()]);
1414 mDeferredBindRunnables.clear();
1415 }
1416 for (final Runnable r : deferredBindRunnables) {
Winson Chung81b52252012-08-27 15:34:29 -07001417 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001418 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001419 }
1420 }
1421
Joe Onorato36115782010-06-17 13:28:48 -04001422 public void stopLoader() {
1423 synchronized (mLock) {
1424 if (mLoaderTask != null) {
1425 mLoaderTask.stopLocked();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001426 }
1427 }
Joe Onorato36115782010-06-17 13:28:48 -04001428 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001429
Winson Chung76828c82013-08-19 15:43:29 -07001430 /** Loads the workspace screens db into a map of Rank -> ScreenId */
1431 private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) {
1432 final ContentResolver contentResolver = context.getContentResolver();
1433 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1434 final Cursor sc = contentResolver.query(screensUri, null, null, null, null);
1435 TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>();
1436
1437 try {
1438 final int idIndex = sc.getColumnIndexOrThrow(
1439 LauncherSettings.WorkspaceScreens._ID);
1440 final int rankIndex = sc.getColumnIndexOrThrow(
1441 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1442 while (sc.moveToNext()) {
1443 try {
1444 long screenId = sc.getLong(idIndex);
1445 int rank = sc.getInt(rankIndex);
Winson Chung76828c82013-08-19 15:43:29 -07001446 orderedScreens.put(rank, screenId);
1447 } catch (Exception e) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001448 Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true);
Winson Chung76828c82013-08-19 15:43:29 -07001449 }
1450 }
1451 } finally {
1452 sc.close();
1453 }
Winson Chunga90303b2013-11-15 13:05:06 -08001454
1455 // Log to disk
1456 Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
1457 ArrayList<String> orderedScreensPairs= new ArrayList<String>();
1458 for (Integer i : orderedScreens.keySet()) {
1459 orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
1460 }
1461 Launcher.addDumpLog(TAG, "11683562 - screens: " +
1462 TextUtils.join(", ", orderedScreensPairs), true);
Winson Chung76828c82013-08-19 15:43:29 -07001463 return orderedScreens;
1464 }
1465
Michael Jurkac57b7a82011-08-09 22:02:20 -07001466 public boolean isAllAppsLoaded() {
1467 return mAllAppsLoaded;
1468 }
1469
Winson Chung36a62fe2012-05-06 18:04:42 -07001470 boolean isLoadingWorkspace() {
1471 synchronized (mLock) {
1472 if (mLoaderTask != null) {
1473 return mLoaderTask.isLoadingWorkspace();
1474 }
1475 }
1476 return false;
1477 }
1478
Joe Onorato36115782010-06-17 13:28:48 -04001479 /**
1480 * Runnable for the thread that loads the contents of the launcher:
1481 * - workspace icons
1482 * - widgets
1483 * - all apps icons
1484 */
1485 private class LoaderTask implements Runnable {
1486 private Context mContext;
Joe Onorato36115782010-06-17 13:28:48 -04001487 private boolean mIsLaunching;
Winson Chung36a62fe2012-05-06 18:04:42 -07001488 private boolean mIsLoadingAndBindingWorkspace;
Joe Onorato36115782010-06-17 13:28:48 -04001489 private boolean mStopped;
1490 private boolean mLoadAndBindStepFinished;
Dan Sandlerd5024042014-01-09 15:01:33 -05001491 private int mFlags;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001492
Winson Chungc3eecff2011-07-11 17:44:15 -07001493 private HashMap<Object, CharSequence> mLabelCache;
Joe Onorato36115782010-06-17 13:28:48 -04001494
Dan Sandlerd5024042014-01-09 15:01:33 -05001495 LoaderTask(Context context, boolean isLaunching, int flags) {
Joe Onorato36115782010-06-17 13:28:48 -04001496 mContext = context;
1497 mIsLaunching = isLaunching;
Winson Chungc3eecff2011-07-11 17:44:15 -07001498 mLabelCache = new HashMap<Object, CharSequence>();
Dan Sandlerd5024042014-01-09 15:01:33 -05001499 mFlags = flags;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001500 }
1501
Joe Onorato36115782010-06-17 13:28:48 -04001502 boolean isLaunching() {
1503 return mIsLaunching;
1504 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001505
Winson Chung36a62fe2012-05-06 18:04:42 -07001506 boolean isLoadingWorkspace() {
1507 return mIsLoadingAndBindingWorkspace;
1508 }
1509
Winson Chungc763c4e2013-07-19 13:49:06 -07001510 /** Returns whether this is an upgrade path */
1511 private boolean loadAndBindWorkspace() {
Winson Chung36a62fe2012-05-06 18:04:42 -07001512 mIsLoadingAndBindingWorkspace = true;
1513
Joe Onorato36115782010-06-17 13:28:48 -04001514 // Load the workspace
Joe Onorato36115782010-06-17 13:28:48 -04001515 if (DEBUG_LOADERS) {
1516 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001517 }
Michael Jurka288a36b2011-07-12 16:53:48 -07001518
Winson Chungc763c4e2013-07-19 13:49:06 -07001519 boolean isUpgradePath = false;
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001520 if (!mWorkspaceLoaded) {
Winson Chungc763c4e2013-07-19 13:49:06 -07001521 isUpgradePath = loadWorkspace();
Reena Lee93f824a2011-09-23 17:20:28 -07001522 synchronized (LoaderTask.this) {
1523 if (mStopped) {
Winson Chungc763c4e2013-07-19 13:49:06 -07001524 return isUpgradePath;
Reena Lee93f824a2011-09-23 17:20:28 -07001525 }
1526 mWorkspaceLoaded = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001527 }
1528 }
1529
Joe Onorato36115782010-06-17 13:28:48 -04001530 // Bind the workspace
Winson Chungc763c4e2013-07-19 13:49:06 -07001531 bindWorkspace(-1, isUpgradePath);
1532 return isUpgradePath;
Joe Onorato36115782010-06-17 13:28:48 -04001533 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001534
Joe Onorato36115782010-06-17 13:28:48 -04001535 private void waitForIdle() {
1536 // Wait until the either we're stopped or the other threads are done.
1537 // This way we don't start loading all apps until the workspace has settled
1538 // down.
1539 synchronized (LoaderTask.this) {
1540 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onoratocc67f472010-06-08 10:54:30 -07001541
Joe Onorato36115782010-06-17 13:28:48 -04001542 mHandler.postIdle(new Runnable() {
1543 public void run() {
1544 synchronized (LoaderTask.this) {
1545 mLoadAndBindStepFinished = true;
1546 if (DEBUG_LOADERS) {
1547 Log.d(TAG, "done with previous binding step");
Daniel Sandler843e8602010-06-07 14:59:01 -04001548 }
Joe Onorato36115782010-06-17 13:28:48 -04001549 LoaderTask.this.notify();
Daniel Sandler843e8602010-06-07 14:59:01 -04001550 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001551 }
Joe Onorato36115782010-06-17 13:28:48 -04001552 });
1553
Michael Jurkac7700af2013-05-14 20:17:58 +02001554 while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
Joe Onorato36115782010-06-17 13:28:48 -04001555 try {
Michael Jurkac7700af2013-05-14 20:17:58 +02001556 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1557 // wait no longer than 1sec at a time
1558 this.wait(1000);
Joe Onorato36115782010-06-17 13:28:48 -04001559 } catch (InterruptedException ex) {
1560 // Ignore
Daniel Sandler843e8602010-06-07 14:59:01 -04001561 }
1562 }
Joe Onorato36115782010-06-17 13:28:48 -04001563 if (DEBUG_LOADERS) {
1564 Log.d(TAG, "waited "
Winson Chungaafa03c2010-06-11 17:34:16 -07001565 + (SystemClock.uptimeMillis()-workspaceWaitTime)
Joe Onorato36115782010-06-17 13:28:48 -04001566 + "ms for previous step to finish binding");
1567 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001568 }
Joe Onorato36115782010-06-17 13:28:48 -04001569 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001570
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001571 void runBindSynchronousPage(int synchronousBindPage) {
Derek Prothro7aff3992013-12-10 14:00:37 -05001572 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001573 // Ensure that we have a valid page index to load synchronously
1574 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1575 "valid page index");
1576 }
1577 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1578 // Ensure that we don't try and bind a specified page when the pages have not been
1579 // loaded already (we should load everything asynchronously in that case)
1580 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1581 }
1582 synchronized (mLock) {
1583 if (mIsLoaderTaskRunning) {
1584 // Ensure that we are never running the background loading at this point since
1585 // we also touch the background collections
1586 throw new RuntimeException("Error! Background loading is already running");
1587 }
1588 }
1589
1590 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1591 // data structures, we can't allow any other thread to touch that data, but because
1592 // this call is synchronous, we can get away with not locking).
1593
Daniel Sandlercc8befa2013-06-11 14:45:48 -04001594 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
Adam Cohena13a2f22012-07-23 14:29:15 -07001595 // operations from the previous activity. We need to ensure that all queued operations
1596 // are executed before any synchronous binding work is done.
1597 mHandler.flush();
1598
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001599 // Divide the set of loaded items into those that we are binding synchronously, and
1600 // everything else that is to be bound normally (asynchronously).
Winson Chungc763c4e2013-07-19 13:49:06 -07001601 bindWorkspace(synchronousBindPage, false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001602 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1603 // arise from that.
1604 onlyBindAllApps();
1605 }
1606
Joe Onorato36115782010-06-17 13:28:48 -04001607 public void run() {
Winson Chungc763c4e2013-07-19 13:49:06 -07001608 boolean isUpgrade = false;
1609
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001610 synchronized (mLock) {
1611 mIsLoaderTaskRunning = true;
1612 }
Joe Onorato36115782010-06-17 13:28:48 -04001613 // Optimize for end-user experience: if the Launcher is up and // running with the
1614 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1615 // workspace first (default).
Joe Onorato36115782010-06-17 13:28:48 -04001616 keep_running: {
Daniel Sandler843e8602010-06-07 14:59:01 -04001617 // Elevate priority when Home launches for the first time to avoid
1618 // starving at boot time. Staring at a blank home is not cool.
1619 synchronized (mLock) {
Winson Chungaac01e12011-08-17 10:37:13 -07001620 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
1621 (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
Daniel Sandler843e8602010-06-07 14:59:01 -04001622 android.os.Process.setThreadPriority(mIsLaunching
1623 ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
1624 }
Winson Chung64359a52013-07-08 17:17:08 -07001625 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
Winson Chungc763c4e2013-07-19 13:49:06 -07001626 isUpgrade = loadAndBindWorkspace();
Daniel Sandler843e8602010-06-07 14:59:01 -04001627
Joe Onorato36115782010-06-17 13:28:48 -04001628 if (mStopped) {
1629 break keep_running;
1630 }
1631
1632 // Whew! Hard work done. Slow us down, and wait until the UI thread has
1633 // settled down.
Daniel Sandler843e8602010-06-07 14:59:01 -04001634 synchronized (mLock) {
1635 if (mIsLaunching) {
Winson Chungaac01e12011-08-17 10:37:13 -07001636 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
Daniel Sandler843e8602010-06-07 14:59:01 -04001637 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1638 }
1639 }
Joe Onorato36115782010-06-17 13:28:48 -04001640 waitForIdle();
Daniel Sandler843e8602010-06-07 14:59:01 -04001641
1642 // second step
Winson Chung64359a52013-07-08 17:17:08 -07001643 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1644 loadAndBindAllApps();
Winson Chung7ed37742011-09-08 15:45:51 -07001645
1646 // Restore the default thread priority after we are done loading items
1647 synchronized (mLock) {
1648 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
1649 }
Joe Onorato36115782010-06-17 13:28:48 -04001650 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001651
Winson Chungaac01e12011-08-17 10:37:13 -07001652 // Update the saved icons if necessary
1653 if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
Winson Chung2abf94d2012-07-18 18:16:38 -07001654 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001655 for (Object key : sBgDbIconCache.keySet()) {
1656 updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
1657 }
1658 sBgDbIconCache.clear();
Winson Chungaac01e12011-08-17 10:37:13 -07001659 }
Winson Chungaac01e12011-08-17 10:37:13 -07001660
Nilesh Agrawal16f3ea82014-01-09 17:14:01 -08001661 if (LauncherAppState.isDisableAllApps()) {
Winson Chungc58497e2013-09-03 17:48:37 -07001662 // Ensure that all the applications that are in the system are
1663 // represented on the home screen.
Winson Chungc58497e2013-09-03 17:48:37 -07001664 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
Winson Chungc58497e2013-09-03 17:48:37 -07001665 verifyApplications();
1666 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001667 }
1668
Joe Onorato36115782010-06-17 13:28:48 -04001669 // Clear out this reference, otherwise we end up holding it until all of the
1670 // callback runnables are done.
1671 mContext = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001672
Joe Onorato36115782010-06-17 13:28:48 -04001673 synchronized (mLock) {
1674 // If we are still the last one to be scheduled, remove ourselves.
1675 if (mLoaderTask == this) {
1676 mLoaderTask = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001677 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001678 mIsLoaderTaskRunning = false;
Joe Onorato36115782010-06-17 13:28:48 -04001679 }
Joe Onorato36115782010-06-17 13:28:48 -04001680 }
1681
1682 public void stopLocked() {
1683 synchronized (LoaderTask.this) {
1684 mStopped = true;
1685 this.notify();
1686 }
1687 }
1688
1689 /**
1690 * Gets the callbacks object. If we've been stopped, or if the launcher object
1691 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1692 * object that was around when the deferred message was scheduled, and if there's
1693 * a new Callbacks object around then also return null. This will save us from
1694 * calling onto it with data that will be ignored.
1695 */
1696 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1697 synchronized (mLock) {
1698 if (mStopped) {
1699 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001700 }
Joe Onorato36115782010-06-17 13:28:48 -04001701
1702 if (mCallbacks == null) {
1703 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001704 }
Joe Onorato36115782010-06-17 13:28:48 -04001705
1706 final Callbacks callbacks = mCallbacks.get();
1707 if (callbacks != oldCallbacks) {
1708 return null;
1709 }
1710 if (callbacks == null) {
1711 Log.w(TAG, "no mCallbacks");
1712 return null;
1713 }
1714
1715 return callbacks;
1716 }
1717 }
1718
Winson Chungc763c4e2013-07-19 13:49:06 -07001719 private void verifyApplications() {
1720 final Context context = mApp.getContext();
1721
1722 // Cross reference all the applications in our apps list with items in the workspace
1723 ArrayList<ItemInfo> tmpInfos;
Michael Jurka695ff6b2013-08-05 12:06:48 +02001724 ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
Winson Chungc763c4e2013-07-19 13:49:06 -07001725 synchronized (sBgLock) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02001726 for (AppInfo app : mBgAllAppsList.data) {
Kenny Guyed131872014-04-30 03:02:21 +01001727 tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
Winson Chungc763c4e2013-07-19 13:49:06 -07001728 if (tmpInfos.isEmpty()) {
1729 // We are missing an application icon, so add this to the workspace
1730 added.add(app);
1731 // This is a rare event, so lets log it
1732 Log.e(TAG, "Missing Application on load: " + app);
1733 }
1734 }
1735 }
1736 if (!added.isEmpty()) {
Adam Cohen76a47a12014-02-05 11:47:43 -08001737 addAndBindAddedWorkspaceApps(context, added);
Winson Chungc763c4e2013-07-19 13:49:06 -07001738 }
1739 }
1740
Joe Onorato36115782010-06-17 13:28:48 -04001741 // check & update map of what's occupied; used to discard overlapping/invalid items
Winson Chunga0b7e862013-09-05 16:03:15 -07001742 private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
Adam Cohenae4409d2013-11-26 10:34:59 -08001743 AtomicBoolean deleteOnInvalidPlacement) {
Winson Chung892c74d2013-08-22 16:15:50 -07001744 LauncherAppState app = LauncherAppState.getInstance();
1745 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Dan Sandler295ae182013-12-10 16:05:47 -05001746 final int countX = (int) grid.numColumns;
1747 final int countY = (int) grid.numRows;
Winson Chung892c74d2013-08-22 16:15:50 -07001748
Adam Cohendcd297f2013-06-18 13:13:40 -07001749 long containerIndex = item.screenId;
Winson Chungf30ad5f2011-08-08 10:55:42 -07001750 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001751 // Return early if we detect that an item is under the hotseat button
1752 if (mCallbacks == null ||
1753 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
Adam Cohenae4409d2013-11-26 10:34:59 -08001754 deleteOnInvalidPlacement.set(true);
Dan Sandler295ae182013-12-10 16:05:47 -05001755 Log.e(TAG, "Error loading shortcut into hotseat " + item
1756 + " into position (" + item.screenId + ":" + item.cellX + ","
1757 + item.cellY + ") occupied by all apps");
Winson Chunga0b7e862013-09-05 16:03:15 -07001758 return false;
1759 }
1760
Dan Sandler295ae182013-12-10 16:05:47 -05001761 final ItemInfo[][] hotseatItems =
1762 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1763
Adam Cohenae4409d2013-11-26 10:34:59 -08001764 if (item.screenId >= grid.numHotseatIcons) {
1765 Log.e(TAG, "Error loading shortcut " + item
1766 + " into hotseat position " + item.screenId
1767 + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1)
1768 + ")");
1769 return false;
1770 }
1771
Dan Sandler295ae182013-12-10 16:05:47 -05001772 if (hotseatItems != null) {
1773 if (hotseatItems[(int) item.screenId][0] != null) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001774 Log.e(TAG, "Error loading shortcut into hotseat " + item
1775 + " into position (" + item.screenId + ":" + item.cellX + ","
1776 + item.cellY + ") occupied by "
1777 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1778 [(int) item.screenId][0]);
1779 return false;
Dan Sandler295ae182013-12-10 16:05:47 -05001780 } else {
1781 hotseatItems[(int) item.screenId][0] = item;
1782 return true;
Adam Cohendcd297f2013-06-18 13:13:40 -07001783 }
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001784 } else {
Adam Cohenae4409d2013-11-26 10:34:59 -08001785 final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001786 items[(int) item.screenId][0] = item;
1787 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001788 return true;
1789 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001790 } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1791 // Skip further checking if it is not the hotseat or workspace container
Daniel Sandler8802e962010-05-26 16:28:16 -04001792 return true;
1793 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001794
Adam Cohendcd297f2013-06-18 13:13:40 -07001795 if (!occupied.containsKey(item.screenId)) {
Winson Chung892c74d2013-08-22 16:15:50 -07001796 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001797 occupied.put(item.screenId, items);
1798 }
1799
Dan Sandler295ae182013-12-10 16:05:47 -05001800 final ItemInfo[][] screens = occupied.get(item.screenId);
Adam Cohenae4409d2013-11-26 10:34:59 -08001801 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1802 item.cellX < 0 || item.cellY < 0 ||
1803 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1804 Log.e(TAG, "Error loading shortcut " + item
1805 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1806 + item.cellX + "," + item.cellY
1807 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1808 return false;
1809 }
1810
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001811 // Check if any workspace icons overlap with each other
Joe Onorato36115782010-06-17 13:28:48 -04001812 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1813 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001814 if (screens[x][y] != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001815 Log.e(TAG, "Error loading shortcut " + item
Adam Cohendcd297f2013-06-18 13:13:40 -07001816 + " into cell (" + containerIndex + "-" + item.screenId + ":"
Joe Onorato36115782010-06-17 13:28:48 -04001817 + x + "," + y
Winson Chungaafa03c2010-06-11 17:34:16 -07001818 + ") occupied by "
Adam Cohendcd297f2013-06-18 13:13:40 -07001819 + screens[x][y]);
Joe Onorato36115782010-06-17 13:28:48 -04001820 return false;
1821 }
1822 }
1823 }
1824 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1825 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001826 screens[x][y] = item;
Joe Onorato36115782010-06-17 13:28:48 -04001827 }
1828 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001829
Joe Onorato36115782010-06-17 13:28:48 -04001830 return true;
1831 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001832
Winson Chungba9c37f2013-08-30 14:11:37 -07001833 /** Clears all the sBg data structures */
1834 private void clearSBgDataStructures() {
1835 synchronized (sBgLock) {
1836 sBgWorkspaceItems.clear();
1837 sBgAppWidgets.clear();
1838 sBgFolders.clear();
1839 sBgItemsIdMap.clear();
1840 sBgDbIconCache.clear();
1841 sBgWorkspaceScreens.clear();
1842 }
1843 }
1844
Dan Sandlerd5024042014-01-09 15:01:33 -05001845 /** Returns whether this is an upgrade path */
Winson Chungc763c4e2013-07-19 13:49:06 -07001846 private boolean loadWorkspace() {
Winson Chung9f9f00b2013-11-15 13:27:00 -08001847 // Log to disk
1848 Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
1849
Joe Onorato36115782010-06-17 13:28:48 -04001850 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001851
Joe Onorato36115782010-06-17 13:28:48 -04001852 final Context context = mContext;
1853 final ContentResolver contentResolver = context.getContentResolver();
1854 final PackageManager manager = context.getPackageManager();
1855 final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
1856 final boolean isSafeMode = manager.isSafeMode();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001857 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
1858 final boolean isSdCardReady = context.registerReceiver(null,
Sunny Goyal05e318d2014-07-29 11:49:35 -07001859 new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
Joe Onorato3c2f7e12009-10-31 19:17:31 -04001860
Winson Chung892c74d2013-08-22 16:15:50 -07001861 LauncherAppState app = LauncherAppState.getInstance();
1862 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1863 int countX = (int) grid.numColumns;
1864 int countY = (int) grid.numRows;
1865
Dan Sandlerd5024042014-01-09 15:01:33 -05001866 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
1867 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
1868 LauncherAppState.getLauncherProvider().deleteDatabase();
1869 }
1870
1871 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1872 // append the user's Launcher2 shortcuts
1873 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
1874 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
1875 } else {
1876 // Make sure the default workspace is loaded
1877 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
Sunny Goyal0fe505b2014-08-06 09:55:36 -07001878 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
Dan Sandlerd5024042014-01-09 15:01:33 -05001879 }
Adam Cohene25af792013-06-06 23:08:25 -07001880
Adam Cohen71483f42014-05-15 14:04:01 -07001881 // This code path is for our old migration code and should no longer be exercised
1882 boolean loadedOldDb = false;
Dan Sandlerf0b8dac2013-11-19 12:21:25 -05001883
Winson Chung9f9f00b2013-11-15 13:27:00 -08001884 // Log to disk
1885 Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
Michael Jurkab85f8a42012-04-25 15:48:32 -07001886
Winson Chung2abf94d2012-07-18 18:16:38 -07001887 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001888 clearSBgDataStructures();
Sunny Goyal94485362014-09-18 16:13:58 -07001889 final HashSet<String> installingPkgs = PackageInstallerCompat
1890 .getInstance(mContext).updateAndGetActiveSessionCache();
Romain Guy5c16f3e2010-01-12 17:24:58 -08001891
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001892 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
Chris Wrenf4d08112014-01-16 18:13:56 -05001893 final ArrayList<Long> restoredRows = new ArrayList<Long>();
Winson Chungc763c4e2013-07-19 13:49:06 -07001894 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
Chris Wrene523e702013-10-09 10:36:55 -04001895 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
Adam Cohene25af792013-06-06 23:08:25 -07001896 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
Daniel Sandler8802e962010-05-26 16:28:16 -04001897
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001898 // +1 for the hotseat (it can be larger than the workspace)
1899 // Load workspace in reverse order to ensure that latest items are loaded first (and
1900 // before any earlier duplicates)
Adam Cohendcd297f2013-06-18 13:13:40 -07001901 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001902
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001903 try {
1904 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1905 final int intentIndex = c.getColumnIndexOrThrow
1906 (LauncherSettings.Favorites.INTENT);
1907 final int titleIndex = c.getColumnIndexOrThrow
1908 (LauncherSettings.Favorites.TITLE);
1909 final int iconTypeIndex = c.getColumnIndexOrThrow(
1910 LauncherSettings.Favorites.ICON_TYPE);
1911 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1912 final int iconPackageIndex = c.getColumnIndexOrThrow(
1913 LauncherSettings.Favorites.ICON_PACKAGE);
1914 final int iconResourceIndex = c.getColumnIndexOrThrow(
1915 LauncherSettings.Favorites.ICON_RESOURCE);
1916 final int containerIndex = c.getColumnIndexOrThrow(
1917 LauncherSettings.Favorites.CONTAINER);
1918 final int itemTypeIndex = c.getColumnIndexOrThrow(
1919 LauncherSettings.Favorites.ITEM_TYPE);
1920 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1921 LauncherSettings.Favorites.APPWIDGET_ID);
Chris Wrenc3919c02013-09-18 09:48:33 -04001922 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1923 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001924 final int screenIndex = c.getColumnIndexOrThrow(
1925 LauncherSettings.Favorites.SCREEN);
1926 final int cellXIndex = c.getColumnIndexOrThrow
1927 (LauncherSettings.Favorites.CELLX);
1928 final int cellYIndex = c.getColumnIndexOrThrow
1929 (LauncherSettings.Favorites.CELLY);
1930 final int spanXIndex = c.getColumnIndexOrThrow
1931 (LauncherSettings.Favorites.SPANX);
1932 final int spanYIndex = c.getColumnIndexOrThrow(
1933 LauncherSettings.Favorites.SPANY);
Chris Wrenf4d08112014-01-16 18:13:56 -05001934 final int restoredIndex = c.getColumnIndexOrThrow(
1935 LauncherSettings.Favorites.RESTORED);
Kenny Guyed131872014-04-30 03:02:21 +01001936 final int profileIdIndex = c.getColumnIndexOrThrow(
1937 LauncherSettings.Favorites.PROFILE_ID);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001938 //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1939 //final int displayModeIndex = c.getColumnIndexOrThrow(
1940 // LauncherSettings.Favorites.DISPLAY_MODE);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001941
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001942 ShortcutInfo info;
1943 String intentDescription;
1944 LauncherAppWidgetInfo appWidgetInfo;
1945 int container;
1946 long id;
1947 Intent intent;
Kenny Guyed131872014-04-30 03:02:21 +01001948 UserHandleCompat user;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001949
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001950 while (!mStopped && c.moveToNext()) {
Adam Cohenae4409d2013-11-26 10:34:59 -08001951 AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001952 try {
1953 int itemType = c.getInt(itemTypeIndex);
Chris Wrenf4d08112014-01-16 18:13:56 -05001954 boolean restored = 0 != c.getInt(restoredIndex);
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001955 boolean allowMissingTarget = false;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001956
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001957 switch (itemType) {
1958 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1959 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Winson Chungee055712013-07-30 14:46:24 -07001960 id = c.getLong(idIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001961 intentDescription = c.getString(intentIndex);
Kenny Guy1317e2d2014-05-08 18:52:50 +01001962 long serialNumber = c.getInt(profileIdIndex);
Kenny Guyed131872014-04-30 03:02:21 +01001963 user = mUserManager.getUserForSerialNumber(serialNumber);
Sunny Goyal34942622014-08-29 17:20:55 -07001964 int promiseType = c.getInt(restoredIndex);
Kenny Guyed131872014-04-30 03:02:21 +01001965 if (user == null) {
1966 // User has been deleted remove the item.
1967 itemsToRemove.add(id);
1968 continue;
1969 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001970 try {
1971 intent = Intent.parseUri(intentDescription, 0);
Winson Chungee055712013-07-30 14:46:24 -07001972 ComponentName cn = intent.getComponent();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001973 if (cn != null && cn.getPackageName() != null) {
1974 boolean validPkg = launcherApps.isPackageEnabledForProfile(
1975 cn.getPackageName(), user);
1976 boolean validComponent = validPkg &&
1977 launcherApps.isActivityEnabledForProfile(cn, user);
1978
1979 if (validComponent) {
1980 if (restored) {
1981 // no special handling necessary for this item
1982 restoredRows.add(id);
1983 restored = false;
1984 }
1985 } else if (validPkg) {
Sunny Goyal34942622014-08-29 17:20:55 -07001986 intent = null;
1987 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
1988 // We allow auto install apps to have their intent
1989 // updated after an install.
1990 intent = manager.getLaunchIntentForPackage(
1991 cn.getPackageName());
1992 if (intent != null) {
1993 ContentValues values = new ContentValues();
1994 values.put(LauncherSettings.Favorites.INTENT,
1995 intent.toUri(0));
1996 String where = BaseColumns._ID + "= ?";
1997 String[] args = {Long.toString(id)};
1998 contentResolver.update(contentUri, values, where, args);
1999 }
2000 }
2001
2002 if (intent == null) {
2003 // The app is installed but the component is no
2004 // longer available.
2005 Launcher.addDumpLog(TAG,
2006 "Invalid component removed: " + cn, true);
2007 itemsToRemove.add(id);
2008 continue;
2009 } else {
2010 // no special handling necessary for this item
2011 restoredRows.add(id);
2012 restored = false;
2013 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002014 } else if (restored) {
2015 // Package is not yet available but might be
2016 // installed later.
Chris Wrenf4d08112014-01-16 18:13:56 -05002017 Launcher.addDumpLog(TAG,
2018 "package not yet restored: " + cn, true);
Sunny Goyal94485362014-09-18 16:13:58 -07002019
2020 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
2021 // Restore has started once.
2022 } else if (installingPkgs.contains(cn.getPackageName())) {
2023 // App restore has started. Update the flag
2024 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
2025 ContentValues values = new ContentValues();
2026 values.put(LauncherSettings.Favorites.RESTORED,
2027 promiseType);
2028 String where = BaseColumns._ID + "= ?";
2029 String[] args = {Long.toString(id)};
2030 contentResolver.update(contentUri, values, where, args);
2031
2032 } else if (REMOVE_UNRESTORED_ICONS) {
2033 Launcher.addDumpLog(TAG,
2034 "Unrestored package removed: " + cn, true);
2035 itemsToRemove.add(id);
2036 continue;
2037 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002038 } else if (isSdCardReady) {
2039 // Do not wait for external media load anymore.
2040 // Log the invalid package, and remove it
2041 Launcher.addDumpLog(TAG,
2042 "Invalid package removed: " + cn, true);
2043 itemsToRemove.add(id);
Chris Wrenf4d08112014-01-16 18:13:56 -05002044 continue;
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002045 } else {
2046 // SdCard is not ready yet. Package might get available,
2047 // once it is ready.
2048 Launcher.addDumpLog(TAG, "Invalid package: " + cn
2049 + " (check again later)", true);
2050 HashSet<String> pkgs = sPendingPackages.get(user);
2051 if (pkgs == null) {
Sameer Padala513edae2014-07-29 16:17:08 -07002052 pkgs = new HashSet<String>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002053 sPendingPackages.put(user, pkgs);
2054 }
2055 pkgs.add(cn.getPackageName());
2056 allowMissingTarget = true;
2057 // Add the icon on the workspace anyway.
Winson Chungee055712013-07-30 14:46:24 -07002058 }
Sunny Goyal938a53d2014-09-05 03:17:45 -07002059 } else if (cn == null) {
2060 // For shortcuts with no component, keep them as they are
2061 restoredRows.add(id);
2062 restored = false;
Winson Chungee055712013-07-30 14:46:24 -07002063 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002064 } catch (URISyntaxException e) {
Chris Wrenf4d08112014-01-16 18:13:56 -05002065 Launcher.addDumpLog(TAG,
2066 "Invalid uri: " + intentDescription, true);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002067 continue;
2068 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002069
Chris Wrenf4d08112014-01-16 18:13:56 -05002070 if (restored) {
Kenny Guyed131872014-04-30 03:02:21 +01002071 if (user.equals(UserHandleCompat.myUserHandle())) {
2072 Launcher.addDumpLog(TAG,
2073 "constructing info for partially restored package",
2074 true);
Sunny Goyal34942622014-08-29 17:20:55 -07002075 info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
Kenny Guyed131872014-04-30 03:02:21 +01002076 intent = getRestoredItemIntent(c, context, intent);
2077 } else {
2078 // Don't restore items for other profiles.
2079 itemsToRemove.add(id);
2080 continue;
2081 }
Chris Wrenf4d08112014-01-16 18:13:56 -05002082 } else if (itemType ==
2083 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002084 info = getShortcutInfo(manager, intent, user, context, c,
2085 iconIndex, titleIndex, mLabelCache, allowMissingTarget);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002086 } else {
2087 info = getShortcutInfo(c, context, iconTypeIndex,
2088 iconPackageIndex, iconResourceIndex, iconIndex,
2089 titleIndex);
Michael Jurka96879562012-03-22 05:54:33 -07002090
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002091 // App shortcuts that used to be automatically added to Launcher
2092 // didn't always have the correct intent flags set, so do that
2093 // here
2094 if (intent.getAction() != null &&
Michael Jurka9ad00562012-05-14 12:24:22 -07002095 intent.getCategories() != null &&
2096 intent.getAction().equals(Intent.ACTION_MAIN) &&
Michael Jurka96879562012-03-22 05:54:33 -07002097 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002098 intent.addFlags(
2099 Intent.FLAG_ACTIVITY_NEW_TASK |
2100 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
2101 }
Michael Jurka96879562012-03-22 05:54:33 -07002102 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002103
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002104 if (info != null) {
Winson Chungee055712013-07-30 14:46:24 -07002105 info.id = id;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002106 info.intent = intent;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002107 container = c.getInt(containerIndex);
2108 info.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07002109 info.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002110 info.cellX = c.getInt(cellXIndex);
2111 info.cellY = c.getInt(cellYIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07002112 info.spanX = 1;
2113 info.spanY = 1;
Kenny Guyed131872014-04-30 03:02:21 +01002114 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
Sunny Goyalc5c60ad2014-07-14 12:02:01 -07002115 info.isDisabled = isSafeMode
2116 && !Utilities.isSystemApp(context, intent);
Adam Cohenae4409d2013-11-26 10:34:59 -08002117
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002118 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08002119 deleteOnInvalidPlacement.set(false);
2120 if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) {
2121 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07002122 itemsToRemove.add(id);
2123 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002124 break;
2125 }
2126
2127 switch (container) {
2128 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2129 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2130 sBgWorkspaceItems.add(info);
2131 break;
2132 default:
2133 // Item is in a user folder
2134 FolderInfo folderInfo =
2135 findOrMakeFolder(sBgFolders, container);
2136 folderInfo.add(info);
2137 break;
2138 }
2139 sBgItemsIdMap.put(info.id, info);
2140
2141 // now that we've loaded everthing re-save it with the
2142 // icon in case it disappears somehow.
2143 queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
Winson Chung1323b482013-08-05 12:41:55 -07002144 } else {
2145 throw new RuntimeException("Unexpected null ShortcutInfo");
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002146 }
2147 break;
2148
2149 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
2150 id = c.getLong(idIndex);
2151 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
2152
2153 folderInfo.title = c.getString(titleIndex);
2154 folderInfo.id = id;
Joe Onorato9c1289c2009-08-17 11:03:03 -04002155 container = c.getInt(containerIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002156 folderInfo.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07002157 folderInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002158 folderInfo.cellX = c.getInt(cellXIndex);
2159 folderInfo.cellY = c.getInt(cellYIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07002160 folderInfo.spanX = 1;
2161 folderInfo.spanY = 1;
Joe Onorato9c1289c2009-08-17 11:03:03 -04002162
Daniel Sandler8802e962010-05-26 16:28:16 -04002163 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08002164 deleteOnInvalidPlacement.set(false);
Winson Chunga0b7e862013-09-05 16:03:15 -07002165 if (!checkItemPlacement(occupied, folderInfo,
Adam Cohenae4409d2013-11-26 10:34:59 -08002166 deleteOnInvalidPlacement)) {
2167 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07002168 itemsToRemove.add(id);
2169 }
Daniel Sandler8802e962010-05-26 16:28:16 -04002170 break;
2171 }
Winson Chung5f8afe62013-08-12 16:19:28 -07002172
Joe Onorato9c1289c2009-08-17 11:03:03 -04002173 switch (container) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002174 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
2175 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
2176 sBgWorkspaceItems.add(folderInfo);
2177 break;
Joe Onorato36115782010-06-17 13:28:48 -04002178 }
Joe Onorato17a89222011-02-08 17:26:11 -08002179
Chris Wrenf4d08112014-01-16 18:13:56 -05002180 if (restored) {
2181 // no special handling required for restored folders
2182 restoredRows.add(id);
2183 }
2184
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002185 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2186 sBgFolders.put(folderInfo.id, folderInfo);
2187 break;
2188
2189 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
2190 // Read all Launcher-specific widget details
2191 int appWidgetId = c.getInt(appWidgetIdIndex);
Chris Wrenc3919c02013-09-18 09:48:33 -04002192 String savedProvider = c.getString(appWidgetProviderIndex);
Joe Onorato36115782010-06-17 13:28:48 -04002193 id = c.getLong(idIndex);
Sunny Goyalff572272014-07-23 13:58:07 -07002194 final ComponentName component =
2195 ComponentName.unflattenFromString(savedProvider);
Joe Onorato36115782010-06-17 13:28:48 -04002196
Sunny Goyal651077b2014-06-30 14:15:31 -07002197 final int restoreStatus = c.getInt(restoredIndex);
Sunny Goyalff572272014-07-23 13:58:07 -07002198 final boolean isIdValid = (restoreStatus &
2199 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
Joe Onorato36115782010-06-17 13:28:48 -04002200
Sunny Goyalff572272014-07-23 13:58:07 -07002201 final boolean wasProviderReady = (restoreStatus &
2202 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
Sunny Goyal651077b2014-06-30 14:15:31 -07002203
Sunny Goyalff572272014-07-23 13:58:07 -07002204 final AppWidgetProviderInfo provider = isIdValid
2205 ? widgets.getAppWidgetInfo(appWidgetId)
2206 : findAppWidgetProviderInfoWithComponent(context, component);
2207
2208 final boolean isProviderReady = isValidProvider(provider);
2209 if (!isSafeMode && wasProviderReady && !isProviderReady) {
Sunny Goyal651077b2014-06-30 14:15:31 -07002210 String log = "Deleting widget that isn't installed anymore: "
Sunny Goyalff572272014-07-23 13:58:07 -07002211 + "id=" + id + " appWidgetId=" + appWidgetId;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002212 Log.e(TAG, log);
Adam Cohen4caf2982013-08-20 18:54:31 -07002213 Launcher.addDumpLog(TAG, log, false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002214 itemsToRemove.add(id);
2215 } else {
Sunny Goyalff572272014-07-23 13:58:07 -07002216 if (isProviderReady) {
Sunny Goyal651077b2014-06-30 14:15:31 -07002217 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2218 provider.provider);
2219 int[] minSpan =
2220 Launcher.getMinSpanForWidget(context, provider);
2221 appWidgetInfo.minSpanX = minSpan[0];
2222 appWidgetInfo.minSpanY = minSpan[1];
Sunny Goyalff572272014-07-23 13:58:07 -07002223
2224 int status = restoreStatus;
2225 if (!wasProviderReady) {
2226 // If provider was not previously ready, update the
2227 // status and UI flag.
2228
2229 // Id would be valid only if the widget restore broadcast was received.
2230 if (isIdValid) {
2231 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
2232 } else {
2233 status &= ~LauncherAppWidgetInfo
2234 .FLAG_PROVIDER_NOT_READY;
2235 }
2236 }
2237 appWidgetInfo.restoreStatus = status;
Sunny Goyal651077b2014-06-30 14:15:31 -07002238 } else {
2239 Log.v(TAG, "Widget restore pending id=" + id
2240 + " appWidgetId=" + appWidgetId
2241 + " status =" + restoreStatus);
2242 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
Sunny Goyalff572272014-07-23 13:58:07 -07002243 component);
Sunny Goyal651077b2014-06-30 14:15:31 -07002244 appWidgetInfo.restoreStatus = restoreStatus;
Sunny Goyal94485362014-09-18 16:13:58 -07002245
2246 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
2247 // Restore has started once.
2248 } else if (installingPkgs.contains(component.getPackageName())) {
2249 // App restore has started. Update the flag
2250 appWidgetInfo.restoreStatus |=
2251 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
2252 } else if (REMOVE_UNRESTORED_ICONS) {
2253 Launcher.addDumpLog(TAG,
2254 "Unrestored package removed: " + component, true);
2255 itemsToRemove.add(id);
2256 continue;
2257 }
Sunny Goyal651077b2014-06-30 14:15:31 -07002258 }
Sunny Goyalff572272014-07-23 13:58:07 -07002259
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002260 appWidgetInfo.id = id;
Adam Cohendcd297f2013-06-18 13:13:40 -07002261 appWidgetInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002262 appWidgetInfo.cellX = c.getInt(cellXIndex);
2263 appWidgetInfo.cellY = c.getInt(cellYIndex);
2264 appWidgetInfo.spanX = c.getInt(spanXIndex);
2265 appWidgetInfo.spanY = c.getInt(spanYIndex);
Joe Onorato36115782010-06-17 13:28:48 -04002266
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002267 container = c.getInt(containerIndex);
2268 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2269 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2270 Log.e(TAG, "Widget found where container != " +
2271 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2272 continue;
2273 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002274
Adam Cohene25af792013-06-06 23:08:25 -07002275 appWidgetInfo.container = c.getInt(containerIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002276 // check & update map of what's occupied
Adam Cohenae4409d2013-11-26 10:34:59 -08002277 deleteOnInvalidPlacement.set(false);
Winson Chunga0b7e862013-09-05 16:03:15 -07002278 if (!checkItemPlacement(occupied, appWidgetInfo,
Adam Cohenae4409d2013-11-26 10:34:59 -08002279 deleteOnInvalidPlacement)) {
2280 if (deleteOnInvalidPlacement.get()) {
Winson Chunga0b7e862013-09-05 16:03:15 -07002281 itemsToRemove.add(id);
2282 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002283 break;
2284 }
Sunny Goyal651077b2014-06-30 14:15:31 -07002285
Sunny Goyal94485362014-09-18 16:13:58 -07002286 String providerName = appWidgetInfo.providerName.flattenToString();
2287 if (!providerName.equals(savedProvider) ||
2288 (appWidgetInfo.restoreStatus != restoreStatus)) {
2289 ContentValues values = new ContentValues();
2290 values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2291 providerName);
2292 values.put(LauncherSettings.Favorites.RESTORED,
2293 appWidgetInfo.restoreStatus);
2294 String where = BaseColumns._ID + "= ?";
2295 String[] args = {Long.toString(id)};
2296 contentResolver.update(contentUri, values, where, args);
Chris Wrenc3919c02013-09-18 09:48:33 -04002297 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002298 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2299 sBgAppWidgets.add(appWidgetInfo);
2300 }
Joe Onorato36115782010-06-17 13:28:48 -04002301 break;
2302 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002303 } catch (Exception e) {
Dan Sandler295ae182013-12-10 16:05:47 -05002304 Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
Romain Guy5c16f3e2010-01-12 17:24:58 -08002305 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002306 }
2307 } finally {
Daniel Sandler47b50312013-07-25 13:16:14 -04002308 if (c != null) {
2309 c.close();
2310 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002311 }
2312
Winson Chungba9c37f2013-08-30 14:11:37 -07002313 // Break early if we've stopped loading
2314 if (mStopped) {
Winson Chungba9c37f2013-08-30 14:11:37 -07002315 clearSBgDataStructures();
2316 return false;
2317 }
2318
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002319 if (itemsToRemove.size() > 0) {
2320 ContentProviderClient client = contentResolver.acquireContentProviderClient(
Adam Cohen4caf2982013-08-20 18:54:31 -07002321 LauncherSettings.Favorites.CONTENT_URI);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002322 // Remove dead items
2323 for (long id : itemsToRemove) {
2324 if (DEBUG_LOADERS) {
2325 Log.d(TAG, "Removed id = " + id);
2326 }
2327 // Don't notify content observers
2328 try {
2329 client.delete(LauncherSettings.Favorites.getContentUri(id, false),
2330 null, null);
2331 } catch (RemoteException e) {
2332 Log.w(TAG, "Could not remove id = " + id);
2333 }
Romain Guy5c16f3e2010-01-12 17:24:58 -08002334 }
2335 }
2336
Chris Wrenf4d08112014-01-16 18:13:56 -05002337 if (restoredRows.size() > 0) {
2338 ContentProviderClient updater = contentResolver.acquireContentProviderClient(
2339 LauncherSettings.Favorites.CONTENT_URI);
2340 // Update restored items that no longer require special handling
2341 try {
2342 StringBuilder selectionBuilder = new StringBuilder();
2343 selectionBuilder.append(LauncherSettings.Favorites._ID);
2344 selectionBuilder.append(" IN (");
2345 selectionBuilder.append(TextUtils.join(", ", restoredRows));
2346 selectionBuilder.append(")");
2347 ContentValues values = new ContentValues();
2348 values.put(LauncherSettings.Favorites.RESTORED, 0);
Sunny Goyal34942622014-08-29 17:20:55 -07002349 updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
Chris Wrenf4d08112014-01-16 18:13:56 -05002350 values, selectionBuilder.toString(), null);
2351 } catch (RemoteException e) {
2352 Log.w(TAG, "Could not update restored rows");
2353 }
2354 }
2355
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002356 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
2357 context.registerReceiver(new AppsAvailabilityCheck(),
Sunny Goyal05e318d2014-07-29 11:49:35 -07002358 new IntentFilter(StartupReceiver.SYSTEM_READY),
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002359 null, sWorker);
2360 }
2361
Winson Chungc763c4e2013-07-19 13:49:06 -07002362 if (loadedOldDb) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002363 long maxScreenId = 0;
2364 // If we're importing we use the old screen order.
2365 for (ItemInfo item: sBgItemsIdMap.values()) {
2366 long screenId = item.screenId;
2367 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2368 !sBgWorkspaceScreens.contains(screenId)) {
2369 sBgWorkspaceScreens.add(screenId);
2370 if (screenId > maxScreenId) {
2371 maxScreenId = screenId;
2372 }
2373 }
2374 }
2375 Collections.sort(sBgWorkspaceScreens);
Winson Chung9f9f00b2013-11-15 13:27:00 -08002376 // Log to disk
2377 Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
2378 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2379 TextUtils.join(", ", sBgWorkspaceScreens), true);
Winson Chung9e6a0a22013-08-27 11:58:12 -07002380
Michael Jurka414300a2013-08-27 15:42:35 +02002381 LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
Adam Cohendcd297f2013-06-18 13:13:40 -07002382 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
Winson Chungc763c4e2013-07-19 13:49:06 -07002383
2384 // Update the max item id after we load an old db
2385 long maxItemId = 0;
2386 // If we're importing we use the old screen order.
2387 for (ItemInfo item: sBgItemsIdMap.values()) {
2388 maxItemId = Math.max(maxItemId, item.id);
2389 }
Michael Jurka414300a2013-08-27 15:42:35 +02002390 LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
Adam Cohendcd297f2013-06-18 13:13:40 -07002391 } else {
Winson Chung76828c82013-08-19 15:43:29 -07002392 TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
2393 for (Integer i : orderedScreens.keySet()) {
2394 sBgWorkspaceScreens.add(orderedScreens.get(i));
Adam Cohendcd297f2013-06-18 13:13:40 -07002395 }
Winson Chung9f9f00b2013-11-15 13:27:00 -08002396 // Log to disk
2397 Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
2398 TextUtils.join(", ", sBgWorkspaceScreens), true);
Adam Cohendcd297f2013-06-18 13:13:40 -07002399
2400 // Remove any empty screens
Winson Chung933bae62013-08-29 11:42:30 -07002401 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002402 for (ItemInfo item: sBgItemsIdMap.values()) {
2403 long screenId = item.screenId;
Adam Cohendcd297f2013-06-18 13:13:40 -07002404 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2405 unusedScreens.contains(screenId)) {
2406 unusedScreens.remove(screenId);
2407 }
2408 }
2409
2410 // If there are any empty screens remove them, and update.
2411 if (unusedScreens.size() != 0) {
Winson Chung9f9f00b2013-11-15 13:27:00 -08002412 // Log to disk
2413 Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
2414 TextUtils.join(", ", unusedScreens), true);
2415
Winson Chung933bae62013-08-29 11:42:30 -07002416 sBgWorkspaceScreens.removeAll(unusedScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002417 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
2418 }
2419 }
2420
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002421 if (DEBUG_LOADERS) {
2422 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2423 Log.d(TAG, "workspace layout: ");
Adam Cohendcd297f2013-06-18 13:13:40 -07002424 int nScreens = occupied.size();
Winson Chung892c74d2013-08-22 16:15:50 -07002425 for (int y = 0; y < countY; y++) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002426 String line = "";
Adam Cohendcd297f2013-06-18 13:13:40 -07002427
Daniel Sandler566da102013-06-25 23:43:45 -04002428 Iterator<Long> iter = occupied.keySet().iterator();
Winson Chungc9168342013-06-26 14:54:55 -07002429 while (iter.hasNext()) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002430 long screenId = iter.next();
Winson Chungc9168342013-06-26 14:54:55 -07002431 if (screenId > 0) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002432 line += " | ";
2433 }
Winson Chung892c74d2013-08-22 16:15:50 -07002434 for (int x = 0; x < countX; x++) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002435 ItemInfo[][] screen = occupied.get(screenId);
2436 if (x < screen.length && y < screen[x].length) {
2437 line += (screen[x][y] != null) ? "#" : ".";
2438 } else {
2439 line += "!";
2440 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002441 }
Joe Onorato36115782010-06-17 13:28:48 -04002442 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002443 Log.d(TAG, "[ " + line + " ]");
Joe Onorato36115782010-06-17 13:28:48 -04002444 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002445 }
Joe Onorato36115782010-06-17 13:28:48 -04002446 }
Winson Chungc763c4e2013-07-19 13:49:06 -07002447 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -07002448 }
2449
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002450 /** Filters the set of items who are directly or indirectly (via another container) on the
2451 * specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002452 private void filterCurrentWorkspaceItems(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002453 ArrayList<ItemInfo> allWorkspaceItems,
2454 ArrayList<ItemInfo> currentScreenItems,
2455 ArrayList<ItemInfo> otherScreenItems) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002456 // Purge any null ItemInfos
2457 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2458 while (iter.hasNext()) {
2459 ItemInfo i = iter.next();
2460 if (i == null) {
2461 iter.remove();
2462 }
2463 }
2464
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002465 // Order the set of items by their containers first, this allows use to walk through the
2466 // list sequentially, build up a list of containers that are in the specified screen,
2467 // as well as all items in those containers.
2468 Set<Long> itemsOnScreen = new HashSet<Long>();
2469 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2470 @Override
2471 public int compare(ItemInfo lhs, ItemInfo rhs) {
2472 return (int) (lhs.container - rhs.container);
2473 }
2474 });
2475 for (ItemInfo info : allWorkspaceItems) {
2476 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Winson Chung9b9fb962013-11-15 15:39:34 -08002477 if (info.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002478 currentScreenItems.add(info);
2479 itemsOnScreen.add(info.id);
2480 } else {
2481 otherScreenItems.add(info);
2482 }
2483 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2484 currentScreenItems.add(info);
2485 itemsOnScreen.add(info.id);
2486 } else {
2487 if (itemsOnScreen.contains(info.container)) {
2488 currentScreenItems.add(info);
2489 itemsOnScreen.add(info.id);
2490 } else {
2491 otherScreenItems.add(info);
2492 }
2493 }
2494 }
2495 }
2496
2497 /** Filters the set of widgets which are on the specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002498 private void filterCurrentAppWidgets(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002499 ArrayList<LauncherAppWidgetInfo> appWidgets,
2500 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2501 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002502
2503 for (LauncherAppWidgetInfo widget : appWidgets) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002504 if (widget == null) continue;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002505 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
Winson Chung9b9fb962013-11-15 15:39:34 -08002506 widget.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002507 currentScreenWidgets.add(widget);
2508 } else {
2509 otherScreenWidgets.add(widget);
2510 }
2511 }
2512 }
2513
2514 /** Filters the set of folders which are on the specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002515 private void filterCurrentFolders(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002516 HashMap<Long, ItemInfo> itemsIdMap,
2517 HashMap<Long, FolderInfo> folders,
2518 HashMap<Long, FolderInfo> currentScreenFolders,
2519 HashMap<Long, FolderInfo> otherScreenFolders) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002520
2521 for (long id : folders.keySet()) {
2522 ItemInfo info = itemsIdMap.get(id);
2523 FolderInfo folder = folders.get(id);
Winson Chung2abf94d2012-07-18 18:16:38 -07002524 if (info == null || folder == null) continue;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002525 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
Winson Chung9b9fb962013-11-15 15:39:34 -08002526 info.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002527 currentScreenFolders.put(id, folder);
2528 } else {
2529 otherScreenFolders.put(id, folder);
2530 }
2531 }
2532 }
2533
2534 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2535 * right) */
2536 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
Winson Chung892c74d2013-08-22 16:15:50 -07002537 final LauncherAppState app = LauncherAppState.getInstance();
2538 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002539 // XXX: review this
2540 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
Winson Chungdb8a8942012-04-03 14:08:41 -07002541 @Override
2542 public int compare(ItemInfo lhs, ItemInfo rhs) {
Winson Chung892c74d2013-08-22 16:15:50 -07002543 int cellCountX = (int) grid.numColumns;
2544 int cellCountY = (int) grid.numRows;
Winson Chungdb8a8942012-04-03 14:08:41 -07002545 int screenOffset = cellCountX * cellCountY;
2546 int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -07002547 long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
Winson Chungdb8a8942012-04-03 14:08:41 -07002548 lhs.cellY * cellCountX + lhs.cellX);
Adam Cohendcd297f2013-06-18 13:13:40 -07002549 long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
Winson Chungdb8a8942012-04-03 14:08:41 -07002550 rhs.cellY * cellCountX + rhs.cellX);
2551 return (int) (lr - rr);
2552 }
2553 });
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002554 }
Winson Chungdb8a8942012-04-03 14:08:41 -07002555
Adam Cohendcd297f2013-06-18 13:13:40 -07002556 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2557 final ArrayList<Long> orderedScreens) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002558 final Runnable r = new Runnable() {
2559 @Override
2560 public void run() {
2561 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2562 if (callbacks != null) {
2563 callbacks.bindScreens(orderedScreens);
2564 }
2565 }
2566 };
2567 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
2568 }
2569
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002570 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2571 final ArrayList<ItemInfo> workspaceItems,
2572 final ArrayList<LauncherAppWidgetInfo> appWidgets,
2573 final HashMap<Long, FolderInfo> folders,
2574 ArrayList<Runnable> deferredBindRunnables) {
Winson Chung603bcb92011-09-02 11:45:39 -07002575
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002576 final boolean postOnMainThread = (deferredBindRunnables != null);
2577
2578 // Bind the workspace items
Winson Chungdb8a8942012-04-03 14:08:41 -07002579 int N = workspaceItems.size();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002580 for (int i = 0; i < N; i += ITEMS_CHUNK) {
Joe Onorato36115782010-06-17 13:28:48 -04002581 final int start = i;
2582 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002583 final Runnable r = new Runnable() {
2584 @Override
Joe Onorato9c1289c2009-08-17 11:03:03 -04002585 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08002586 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002587 if (callbacks != null) {
Winson Chung64359a52013-07-08 17:17:08 -07002588 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2589 false);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002590 }
2591 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002592 };
2593 if (postOnMainThread) {
Jason Monka0a7a742014-04-22 09:23:19 -04002594 synchronized (deferredBindRunnables) {
2595 deferredBindRunnables.add(r);
2596 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002597 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002598 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002599 }
Joe Onorato36115782010-06-17 13:28:48 -04002600 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002601
2602 // Bind the folders
2603 if (!folders.isEmpty()) {
2604 final Runnable r = new Runnable() {
2605 public void run() {
2606 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2607 if (callbacks != null) {
2608 callbacks.bindFolders(folders);
2609 }
2610 }
2611 };
2612 if (postOnMainThread) {
Jason Monka0a7a742014-04-22 09:23:19 -04002613 synchronized (deferredBindRunnables) {
2614 deferredBindRunnables.add(r);
2615 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002616 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002617 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002618 }
2619 }
2620
2621 // Bind the widgets, one at a time
2622 N = appWidgets.size();
2623 for (int i = 0; i < N; i++) {
2624 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2625 final Runnable r = new Runnable() {
2626 public void run() {
2627 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2628 if (callbacks != null) {
2629 callbacks.bindAppWidget(widget);
2630 }
2631 }
2632 };
2633 if (postOnMainThread) {
2634 deferredBindRunnables.add(r);
2635 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002636 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002637 }
2638 }
2639 }
2640
2641 /**
2642 * Binds all loaded data to actual views on the main thread.
2643 */
Winson Chungc763c4e2013-07-19 13:49:06 -07002644 private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002645 final long t = SystemClock.uptimeMillis();
2646 Runnable r;
2647
2648 // Don't use these two variables in any of the callback runnables.
2649 // Otherwise we hold a reference to them.
2650 final Callbacks oldCallbacks = mCallbacks.get();
2651 if (oldCallbacks == null) {
2652 // This launcher has exited and nobody bothered to tell us. Just bail.
2653 Log.w(TAG, "LoaderTask running with no launcher");
2654 return;
2655 }
2656
Winson Chung9b9fb962013-11-15 15:39:34 -08002657 // Save a copy of all the bg-thread collections
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002658 ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
2659 ArrayList<LauncherAppWidgetInfo> appWidgets =
2660 new ArrayList<LauncherAppWidgetInfo>();
2661 HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
2662 HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
Adam Cohendcd297f2013-06-18 13:13:40 -07002663 ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
Winson Chung2abf94d2012-07-18 18:16:38 -07002664 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002665 workspaceItems.addAll(sBgWorkspaceItems);
2666 appWidgets.addAll(sBgAppWidgets);
2667 folders.putAll(sBgFolders);
2668 itemsIdMap.putAll(sBgItemsIdMap);
Adam Cohendcd297f2013-06-18 13:13:40 -07002669 orderedScreenIds.addAll(sBgWorkspaceScreens);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002670 }
2671
Derek Prothro7aff3992013-12-10 14:00:37 -05002672 final boolean isLoadingSynchronously =
2673 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
Adam Cohend8dbb462013-11-27 11:55:48 -08002674 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
Winson Chung9b9fb962013-11-15 15:39:34 -08002675 oldCallbacks.getCurrentWorkspaceScreen();
Adam Cohend8dbb462013-11-27 11:55:48 -08002676 if (currScreen >= orderedScreenIds.size()) {
2677 // There may be no workspace screens (just hotseat items and an empty page).
Derek Prothro7aff3992013-12-10 14:00:37 -05002678 currScreen = PagedView.INVALID_RESTORE_PAGE;
Winson Chung9b9fb962013-11-15 15:39:34 -08002679 }
Adam Cohend8dbb462013-11-27 11:55:48 -08002680 final int currentScreen = currScreen;
Derek Prothro7aff3992013-12-10 14:00:37 -05002681 final long currentScreenId = currentScreen < 0
2682 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
Winson Chung9b9fb962013-11-15 15:39:34 -08002683
2684 // Load all the items that are on the current page first (and in the process, unbind
2685 // all the existing workspace items before we call startBinding() below.
2686 unbindWorkspaceItemsOnMainThread();
2687
2688 // Separate the items that are on the current screen, and all the other remaining items
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002689 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
2690 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
2691 ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
2692 new ArrayList<LauncherAppWidgetInfo>();
2693 ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
2694 new ArrayList<LauncherAppWidgetInfo>();
2695 HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
2696 HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
2697
Winson Chung9b9fb962013-11-15 15:39:34 -08002698 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002699 otherWorkspaceItems);
Winson Chung9b9fb962013-11-15 15:39:34 -08002700 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002701 otherAppWidgets);
Winson Chung9b9fb962013-11-15 15:39:34 -08002702 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002703 otherFolders);
2704 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2705 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2706
2707 // Tell the workspace that we're about to start binding items
2708 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002709 public void run() {
2710 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2711 if (callbacks != null) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002712 callbacks.startBinding();
Joe Onorato36115782010-06-17 13:28:48 -04002713 }
2714 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002715 };
Winson Chung81b52252012-08-27 15:34:29 -07002716 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002717
Adam Cohendcd297f2013-06-18 13:13:40 -07002718 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2719
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002720 // Load items on the current page
2721 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
2722 currentFolders, null);
Adam Cohen1462de32012-07-24 22:34:36 -07002723 if (isLoadingSynchronously) {
2724 r = new Runnable() {
2725 public void run() {
2726 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Derek Prothro7aff3992013-12-10 14:00:37 -05002727 if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
Adam Cohen1462de32012-07-24 22:34:36 -07002728 callbacks.onPageBoundSynchronously(currentScreen);
2729 }
2730 }
2731 };
Winson Chung81b52252012-08-27 15:34:29 -07002732 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Adam Cohen1462de32012-07-24 22:34:36 -07002733 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002734
Winson Chung4a2afa32012-07-19 14:53:05 -07002735 // Load all the remaining pages (if we are loading synchronously, we want to defer this
2736 // work until after the first render)
Jason Monka0a7a742014-04-22 09:23:19 -04002737 synchronized (mDeferredBindRunnables) {
2738 mDeferredBindRunnables.clear();
2739 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002740 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
Winson Chung4a2afa32012-07-19 14:53:05 -07002741 (isLoadingSynchronously ? mDeferredBindRunnables : null));
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002742
2743 // Tell the workspace that we're done binding items
2744 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002745 public void run() {
2746 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2747 if (callbacks != null) {
Winson Chungc763c4e2013-07-19 13:49:06 -07002748 callbacks.finishBindingItems(isUpgradePath);
Joe Onorato36115782010-06-17 13:28:48 -04002749 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002750
Winson Chung98e030b2012-05-07 16:01:11 -07002751 // If we're profiling, ensure this is the last thing in the queue.
Joe Onorato36115782010-06-17 13:28:48 -04002752 if (DEBUG_LOADERS) {
2753 Log.d(TAG, "bound workspace in "
2754 + (SystemClock.uptimeMillis()-t) + "ms");
2755 }
Winson Chung36a62fe2012-05-06 18:04:42 -07002756
2757 mIsLoadingAndBindingWorkspace = false;
Joe Onorato36115782010-06-17 13:28:48 -04002758 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002759 };
Winson Chung4a2afa32012-07-19 14:53:05 -07002760 if (isLoadingSynchronously) {
Jason Monka0a7a742014-04-22 09:23:19 -04002761 synchronized (mDeferredBindRunnables) {
2762 mDeferredBindRunnables.add(r);
2763 }
Winson Chung4a2afa32012-07-19 14:53:05 -07002764 } else {
Winson Chung81b52252012-08-27 15:34:29 -07002765 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
Winson Chung4a2afa32012-07-19 14:53:05 -07002766 }
Joe Onorato36115782010-06-17 13:28:48 -04002767 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002768
Joe Onorato36115782010-06-17 13:28:48 -04002769 private void loadAndBindAllApps() {
2770 if (DEBUG_LOADERS) {
2771 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2772 }
2773 if (!mAllAppsLoaded) {
Winson Chung64359a52013-07-08 17:17:08 -07002774 loadAllApps();
Reena Lee93f824a2011-09-23 17:20:28 -07002775 synchronized (LoaderTask.this) {
2776 if (mStopped) {
2777 return;
2778 }
2779 mAllAppsLoaded = true;
Joe Onoratocc67f472010-06-08 10:54:30 -07002780 }
Joe Onorato36115782010-06-17 13:28:48 -04002781 } else {
2782 onlyBindAllApps();
2783 }
2784 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002785
Joe Onorato36115782010-06-17 13:28:48 -04002786 private void onlyBindAllApps() {
2787 final Callbacks oldCallbacks = mCallbacks.get();
2788 if (oldCallbacks == null) {
2789 // This launcher has exited and nobody bothered to tell us. Just bail.
2790 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2791 return;
2792 }
2793
2794 // shallow copy
Winson Chungc208ff92012-03-29 17:37:41 -07002795 @SuppressWarnings("unchecked")
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002796 final ArrayList<AppInfo> list
2797 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
Winson Chungc93e5ae2012-07-23 20:48:26 -07002798 Runnable r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002799 public void run() {
2800 final long t = SystemClock.uptimeMillis();
2801 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2802 if (callbacks != null) {
2803 callbacks.bindAllApplications(list);
2804 }
2805 if (DEBUG_LOADERS) {
2806 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
2807 + (SystemClock.uptimeMillis()-t) + "ms");
2808 }
2809 }
Winson Chungc93e5ae2012-07-23 20:48:26 -07002810 };
2811 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
Winson Chung64359a52013-07-08 17:17:08 -07002812 if (isRunningOnMainThread) {
Winson Chungc93e5ae2012-07-23 20:48:26 -07002813 r.run();
2814 } else {
2815 mHandler.post(r);
2816 }
Joe Onorato36115782010-06-17 13:28:48 -04002817 }
2818
Winson Chung64359a52013-07-08 17:17:08 -07002819 private void loadAllApps() {
2820 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato36115782010-06-17 13:28:48 -04002821
Joe Onorato36115782010-06-17 13:28:48 -04002822 final Callbacks oldCallbacks = mCallbacks.get();
2823 if (oldCallbacks == null) {
2824 // This launcher has exited and nobody bothered to tell us. Just bail.
Winson Chung64359a52013-07-08 17:17:08 -07002825 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
Joe Onorato36115782010-06-17 13:28:48 -04002826 return;
2827 }
2828
2829 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
2830 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
2831
Kenny Guyed131872014-04-30 03:02:21 +01002832 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2833
Winson Chung64359a52013-07-08 17:17:08 -07002834 // Clear the list of apps
2835 mBgAllAppsList.clear();
Kenny Guyed131872014-04-30 03:02:21 +01002836 for (UserHandleCompat user : profiles) {
2837 // Query for the set of apps
2838 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2839 List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
2840 if (DEBUG_LOADERS) {
2841 Log.d(TAG, "getActivityList took "
2842 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
2843 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
2844 }
2845 // Fail if we don't have any apps
2846 if (apps == null || apps.isEmpty()) {
2847 return;
2848 }
2849 // Sort the applications by name
2850 final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
2851 Collections.sort(apps,
2852 new LauncherModel.ShortcutNameComparator(mLabelCache));
2853 if (DEBUG_LOADERS) {
2854 Log.d(TAG, "sort took "
2855 + (SystemClock.uptimeMillis()-sortTime) + "ms");
2856 }
Joe Onorato36115782010-06-17 13:28:48 -04002857
Kenny Guyed131872014-04-30 03:02:21 +01002858 // Create the ApplicationInfos
2859 for (int i = 0; i < apps.size(); i++) {
2860 LauncherActivityInfoCompat app = apps.get(i);
2861 // This builds the icon bitmaps.
2862 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
2863 }
Winson Chung64359a52013-07-08 17:17:08 -07002864 }
Bjorn Bringert85f418d2013-09-06 12:50:05 +01002865 // Huh? Shouldn't this be inside the Runnable below?
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002866 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2867 mBgAllAppsList.added = new ArrayList<AppInfo>();
Winson Chung64359a52013-07-08 17:17:08 -07002868
2869 // Post callback on main thread
2870 mHandler.post(new Runnable() {
2871 public void run() {
2872 final long bindTime = SystemClock.uptimeMillis();
Winson Chung11a1a532013-09-13 11:14:45 -07002873 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Winson Chung64359a52013-07-08 17:17:08 -07002874 if (callbacks != null) {
2875 callbacks.bindAllApplications(added);
2876 if (DEBUG_LOADERS) {
2877 Log.d(TAG, "bound " + added.size() + " apps in "
2878 + (SystemClock.uptimeMillis() - bindTime) + "ms");
2879 }
2880 } else {
2881 Log.i(TAG, "not binding apps: no Launcher activity");
2882 }
2883 }
2884 });
2885
Joe Onorato36115782010-06-17 13:28:48 -04002886 if (DEBUG_LOADERS) {
Winson Chung64359a52013-07-08 17:17:08 -07002887 Log.d(TAG, "Icons processed in "
2888 + (SystemClock.uptimeMillis() - loadTime) + "ms");
Joe Onoratobe386092009-11-17 17:32:16 -08002889 }
2890 }
2891
2892 public void dumpState() {
Winson Chung2abf94d2012-07-18 18:16:38 -07002893 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002894 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
2895 Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
2896 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2897 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2898 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2899 }
Joe Onorato36115782010-06-17 13:28:48 -04002900 }
2901 }
2902
2903 void enqueuePackageUpdated(PackageUpdatedTask task) {
Brad Fitzpatrick700889f2010-10-11 09:40:44 -07002904 sWorker.post(task);
Joe Onorato36115782010-06-17 13:28:48 -04002905 }
2906
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002907 private class AppsAvailabilityCheck extends BroadcastReceiver {
2908
2909 @Override
2910 public void onReceive(Context context, Intent intent) {
2911 synchronized (sBgLock) {
2912 final LauncherAppsCompat launcherApps = LauncherAppsCompat
2913 .getInstance(mApp.getContext());
2914 ArrayList<String> packagesRemoved;
2915 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2916 UserHandleCompat user = entry.getKey();
Sameer Padala513edae2014-07-29 16:17:08 -07002917 packagesRemoved = new ArrayList<String>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002918 for (String pkg : entry.getValue()) {
2919 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
2920 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
2921 packagesRemoved.add(pkg);
2922 }
2923 }
2924 if (!packagesRemoved.isEmpty()) {
2925 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
2926 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
2927 }
2928 }
Sunny Goyal34942622014-08-29 17:20:55 -07002929 sPendingPackages.clear();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002930 }
2931 }
2932 }
2933
Joe Onorato36115782010-06-17 13:28:48 -04002934 private class PackageUpdatedTask implements Runnable {
2935 int mOp;
2936 String[] mPackages;
Kenny Guyed131872014-04-30 03:02:21 +01002937 UserHandleCompat mUser;
Joe Onorato36115782010-06-17 13:28:48 -04002938
2939 public static final int OP_NONE = 0;
2940 public static final int OP_ADD = 1;
2941 public static final int OP_UPDATE = 2;
2942 public static final int OP_REMOVE = 3; // uninstlled
2943 public static final int OP_UNAVAILABLE = 4; // external media unmounted
2944
2945
Kenny Guyed131872014-04-30 03:02:21 +01002946 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
Joe Onorato36115782010-06-17 13:28:48 -04002947 mOp = op;
2948 mPackages = packages;
Kenny Guyed131872014-04-30 03:02:21 +01002949 mUser = user;
Joe Onorato36115782010-06-17 13:28:48 -04002950 }
2951
2952 public void run() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -04002953 final Context context = mApp.getContext();
Joe Onorato36115782010-06-17 13:28:48 -04002954
2955 final String[] packages = mPackages;
2956 final int N = packages.length;
2957 switch (mOp) {
2958 case OP_ADD:
2959 for (int i=0; i<N; i++) {
2960 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
Kenny Guyed131872014-04-30 03:02:21 +01002961 mIconCache.remove(packages[i], mUser);
2962 mBgAllAppsList.addPackage(context, packages[i], mUser);
Joe Onorato36115782010-06-17 13:28:48 -04002963 }
2964 break;
2965 case OP_UPDATE:
2966 for (int i=0; i<N; i++) {
2967 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
Kenny Guyed131872014-04-30 03:02:21 +01002968 mBgAllAppsList.updatePackage(context, packages[i], mUser);
Michael Jurkaeb1bb922013-09-26 11:29:01 -07002969 WidgetPreviewLoader.removePackageFromDb(
Daniel Sandlere4f98912013-06-25 15:13:26 -04002970 mApp.getWidgetPreviewCacheDb(), packages[i]);
Joe Onorato36115782010-06-17 13:28:48 -04002971 }
2972 break;
2973 case OP_REMOVE:
2974 case OP_UNAVAILABLE:
2975 for (int i=0; i<N; i++) {
2976 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
Kenny Guyed131872014-04-30 03:02:21 +01002977 mBgAllAppsList.removePackage(packages[i], mUser);
Michael Jurkaeb1bb922013-09-26 11:29:01 -07002978 WidgetPreviewLoader.removePackageFromDb(
Daniel Sandlere4f98912013-06-25 15:13:26 -04002979 mApp.getWidgetPreviewCacheDb(), packages[i]);
Joe Onorato36115782010-06-17 13:28:48 -04002980 }
2981 break;
2982 }
2983
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002984 ArrayList<AppInfo> added = null;
2985 ArrayList<AppInfo> modified = null;
2986 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
Joe Onorato36115782010-06-17 13:28:48 -04002987
Adam Cohen487f7dd2012-06-28 18:12:10 -07002988 if (mBgAllAppsList.added.size() > 0) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002989 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
Winson Chung5d55f332012-07-16 20:45:03 -07002990 mBgAllAppsList.added.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002991 }
Adam Cohen487f7dd2012-06-28 18:12:10 -07002992 if (mBgAllAppsList.modified.size() > 0) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002993 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
Winson Chung5d55f332012-07-16 20:45:03 -07002994 mBgAllAppsList.modified.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002995 }
Winson Chung5d55f332012-07-16 20:45:03 -07002996 if (mBgAllAppsList.removed.size() > 0) {
Winson Chung83892cc2013-05-01 16:53:33 -07002997 removedApps.addAll(mBgAllAppsList.removed);
Winson Chung5d55f332012-07-16 20:45:03 -07002998 mBgAllAppsList.removed.clear();
Winson Chungcd810732012-06-18 16:45:43 -07002999 }
3000
Joe Onorato36115782010-06-17 13:28:48 -04003001 final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
3002 if (callbacks == null) {
3003 Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
3004 return;
3005 }
3006
3007 if (added != null) {
Winson Chung64359a52013-07-08 17:17:08 -07003008 // Ensure that we add all the workspace applications to the db
Adam Cohen76a47a12014-02-05 11:47:43 -08003009 if (LauncherAppState.isDisableAllApps()) {
Winson Chung94d67682013-09-25 16:29:40 -07003010 final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
Adam Cohen76a47a12014-02-05 11:47:43 -08003011 addAndBindAddedWorkspaceApps(context, addedInfos);
3012 } else {
3013 addAppsToAllApps(context, added);
Winson Chung94d67682013-09-25 16:29:40 -07003014 }
Joe Onorato36115782010-06-17 13:28:48 -04003015 }
Adam Cohen76a47a12014-02-05 11:47:43 -08003016
Joe Onorato36115782010-06-17 13:28:48 -04003017 if (modified != null) {
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003018 final ArrayList<AppInfo> modifiedFinal = modified;
Winson Chung64359a52013-07-08 17:17:08 -07003019
3020 // Update the launcher db to reflect the changes
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003021 for (AppInfo a : modifiedFinal) {
Winson Chung64359a52013-07-08 17:17:08 -07003022 ArrayList<ItemInfo> infos =
Kenny Guyed131872014-04-30 03:02:21 +01003023 getItemInfoForComponentName(a.componentName, mUser);
Winson Chung64359a52013-07-08 17:17:08 -07003024 for (ItemInfo i : infos) {
3025 if (isShortcutInfoUpdateable(i)) {
3026 ShortcutInfo info = (ShortcutInfo) i;
3027 info.title = a.title.toString();
Kenny Guyc2bd8102014-06-30 12:30:31 +01003028 info.contentDescription = a.contentDescription;
Winson Chung64359a52013-07-08 17:17:08 -07003029 updateItemInDatabase(context, info);
3030 }
3031 }
3032 }
3033
Joe Onorato36115782010-06-17 13:28:48 -04003034 mHandler.post(new Runnable() {
3035 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07003036 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3037 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04003038 callbacks.bindAppsUpdated(modifiedFinal);
3039 }
3040 }
3041 });
3042 }
Winson Chung83892cc2013-05-01 16:53:33 -07003043
Winson Chungdf95eb12013-10-16 14:57:07 -07003044 final ArrayList<String> removedPackageNames =
3045 new ArrayList<String>();
3046 if (mOp == OP_REMOVE) {
3047 // Mark all packages in the broadcast to be removed
3048 removedPackageNames.addAll(Arrays.asList(packages));
3049 } else if (mOp == OP_UPDATE) {
3050 // Mark disabled packages in the broadcast to be removed
3051 final PackageManager pm = context.getPackageManager();
3052 for (int i=0; i<N; i++) {
Kenny Guyed131872014-04-30 03:02:21 +01003053 if (isPackageDisabled(context, packages[i], mUser)) {
Winson Chungdf95eb12013-10-16 14:57:07 -07003054 removedPackageNames.add(packages[i]);
Winson Chung64359a52013-07-08 17:17:08 -07003055 }
3056 }
Winson Chungdf95eb12013-10-16 14:57:07 -07003057 }
3058 // Remove all the components associated with this package
3059 for (String pn : removedPackageNames) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003060 deletePackageFromDatabase(context, pn, mUser);
Winson Chungdf95eb12013-10-16 14:57:07 -07003061 }
3062 // Remove all the specific components
3063 for (AppInfo a : removedApps) {
Kenny Guyed131872014-04-30 03:02:21 +01003064 ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003065 deleteItemsFromDatabase(context, infos);
Winson Chungdf95eb12013-10-16 14:57:07 -07003066 }
3067 if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
3068 // Remove any queued items from the install queue
3069 String spKey = LauncherAppState.getSharedPreferencesKey();
3070 SharedPreferences sp =
3071 context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
3072 InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
3073 // Call the components-removed callback
Joe Onorato36115782010-06-17 13:28:48 -04003074 mHandler.post(new Runnable() {
3075 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07003076 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3077 if (callbacks == cb && cb != null) {
Kenny Guyed131872014-04-30 03:02:21 +01003078 callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
Joe Onorato36115782010-06-17 13:28:48 -04003079 }
3080 }
3081 });
Joe Onoratobe386092009-11-17 17:32:16 -08003082 }
Winson Chung80baf5a2010-08-09 16:03:15 -07003083
Michael Jurkac402cd92013-05-20 15:49:32 +02003084 final ArrayList<Object> widgetsAndShortcuts =
Kenny Guyed131872014-04-30 03:02:21 +01003085 getSortedWidgetsAndShortcuts(context);
Winson Chung80baf5a2010-08-09 16:03:15 -07003086 mHandler.post(new Runnable() {
3087 @Override
3088 public void run() {
Winson Chungcd2b0142011-06-08 16:02:26 -07003089 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3090 if (callbacks == cb && cb != null) {
Michael Jurkac402cd92013-05-20 15:49:32 +02003091 callbacks.bindPackagesUpdated(widgetsAndShortcuts);
Winson Chung80baf5a2010-08-09 16:03:15 -07003092 }
3093 }
3094 });
Adam Cohen4caf2982013-08-20 18:54:31 -07003095
3096 // Write all the logs to disk
Adam Cohen4caf2982013-08-20 18:54:31 -07003097 mHandler.post(new Runnable() {
3098 public void run() {
3099 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
3100 if (callbacks == cb && cb != null) {
Winson Chungede41292013-09-19 16:27:36 -07003101 callbacks.dumpLogsToLocalData();
Adam Cohen4caf2982013-08-20 18:54:31 -07003102 }
3103 }
3104 });
Joe Onorato9c1289c2009-08-17 11:03:03 -04003105 }
3106 }
3107
Michael Jurkac402cd92013-05-20 15:49:32 +02003108 // Returns a list of ResolveInfos/AppWindowInfos in sorted order
3109 public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
3110 PackageManager packageManager = context.getPackageManager();
3111 final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
Sunny Goyalffe83f12014-08-14 17:39:34 -07003112 widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
3113
Michael Jurkac402cd92013-05-20 15:49:32 +02003114 Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
3115 widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
Sunny Goyalffe83f12014-08-14 17:39:34 -07003116 Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
Michael Jurkac402cd92013-05-20 15:49:32 +02003117 return widgetsAndShortcuts;
3118 }
3119
Kenny Guyed131872014-04-30 03:02:21 +01003120 private static boolean isPackageDisabled(Context context, String packageName,
3121 UserHandleCompat user) {
3122 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3123 return !launcherApps.isPackageEnabledForProfile(packageName, user);
Winson Chungdf95eb12013-10-16 14:57:07 -07003124 }
Adam Cohen556f6132014-01-15 15:18:08 -08003125
Kenny Guyed131872014-04-30 03:02:21 +01003126 public static boolean isValidPackageActivity(Context context, ComponentName cn,
3127 UserHandleCompat user) {
Winson Chungee055712013-07-30 14:46:24 -07003128 if (cn == null) {
3129 return false;
3130 }
Kenny Guyed131872014-04-30 03:02:21 +01003131 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3132 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
Winson Chungdf95eb12013-10-16 14:57:07 -07003133 return false;
3134 }
Kenny Guyed131872014-04-30 03:02:21 +01003135 return launcherApps.isActivityEnabledForProfile(cn, user);
Winson Chungee055712013-07-30 14:46:24 -07003136 }
3137
Adam Cohena28b78e2014-05-20 17:03:04 -07003138 public static boolean isValidPackage(Context context, String packageName,
3139 UserHandleCompat user) {
3140 if (packageName == null) {
3141 return false;
3142 }
3143 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3144 return launcherApps.isPackageEnabledForProfile(packageName, user);
3145 }
3146
Joe Onorato9c1289c2009-08-17 11:03:03 -04003147 /**
Chris Wrenf4d08112014-01-16 18:13:56 -05003148 * Make an ShortcutInfo object for a restored application or shortcut item that points
3149 * to a package that is not yet installed on the system.
3150 */
Sunny Goyal34942622014-08-29 17:20:55 -07003151 public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
3152 int promiseType) {
Chris Wrenf4d08112014-01-16 18:13:56 -05003153 final ShortcutInfo info = new ShortcutInfo();
Kenny Guyed131872014-04-30 03:02:21 +01003154 info.user = UserHandleCompat.myUserHandle();
Sunny Goyal34942622014-08-29 17:20:55 -07003155 mIconCache.getTitleAndIcon(info, intent, info.user, true);
3156
3157 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
3158 String title = (cursor != null) ? cursor.getString(titleIndex) : null;
3159 if (!TextUtils.isEmpty(title)) {
3160 info.title = title;
3161 }
3162 info.status = ShortcutInfo.FLAG_RESTORED_ICON;
3163 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
3164 if (TextUtils.isEmpty(info.title)) {
3165 info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
3166 }
3167 info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
3168 } else {
3169 throw new InvalidParameterException("Invalid restoreType " + promiseType);
3170 }
3171
Kenny Guyc2bd8102014-06-30 12:30:31 +01003172 info.contentDescription = mUserManager.getBadgedLabelForUser(
3173 info.title.toString(), info.user);
Chris Wrenf4d08112014-01-16 18:13:56 -05003174 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
Sunny Goyal34942622014-08-29 17:20:55 -07003175 info.promisedIntent = intent;
Chris Wrenf4d08112014-01-16 18:13:56 -05003176 return info;
3177 }
3178
3179 /**
3180 * Make an Intent object for a restored application or shortcut item that points
3181 * to the market page for the item.
3182 */
3183 private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
3184 ComponentName componentName = intent.getComponent();
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003185 return getMarketIntent(componentName.getPackageName());
3186 }
3187
3188 static Intent getMarketIntent(String packageName) {
3189 return new Intent(Intent.ACTION_VIEW)
3190 .setData(new Uri.Builder()
Chris Wrenf4d08112014-01-16 18:13:56 -05003191 .scheme("market")
3192 .authority("details")
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003193 .appendQueryParameter("id", packageName)
3194 .build());
Chris Wrenf4d08112014-01-16 18:13:56 -05003195 }
3196
3197 /**
Joe Onorato56d82912010-03-07 14:32:10 -05003198 * This is called from the code that adds shortcuts from the intent receiver. This
3199 * doesn't have a Cursor, but
Joe Onorato9c1289c2009-08-17 11:03:03 -04003200 */
Kenny Guyed131872014-04-30 03:02:21 +01003201 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3202 UserHandleCompat user, Context context) {
Sunny Goyalf599ccf2014-07-08 13:01:29 -07003203 return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
Joe Onorato56d82912010-03-07 14:32:10 -05003204 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003205
Joe Onorato56d82912010-03-07 14:32:10 -05003206 /**
3207 * Make an ShortcutInfo object for a shortcut that is an application.
3208 *
3209 * If c is not null, then it will be used to fill in missing data like the title and icon.
3210 */
Kenny Guyed131872014-04-30 03:02:21 +01003211 public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
3212 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
Sunny Goyalf599ccf2014-07-08 13:01:29 -07003213 HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
Kenny Guyed131872014-04-30 03:02:21 +01003214 if (user == null) {
3215 Log.d(TAG, "Null user found in getShortcutInfo");
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003216 return null;
3217 }
3218
Kenny Guyed131872014-04-30 03:02:21 +01003219 ComponentName componentName = intent.getComponent();
3220 if (componentName == null) {
3221 Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
3222 return null;
3223 }
3224
3225 Intent newIntent = new Intent(intent.getAction(), null);
3226 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3227 newIntent.setComponent(componentName);
3228 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
Sunny Goyalf599ccf2014-07-08 13:01:29 -07003229 if ((lai == null) && !allowMissingTarget) {
Kenny Guyed131872014-04-30 03:02:21 +01003230 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
3231 return null;
3232 }
3233
3234 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07003235
Joe Onorato56d82912010-03-07 14:32:10 -05003236 // the resource -- This may implicitly give us back the fallback icon,
3237 // but don't worry about that. All we're doing with usingFallbackIcon is
3238 // to avoid saving lots of copies of that in the database, and most apps
3239 // have icons anyway.
Kenny Guyed131872014-04-30 03:02:21 +01003240 Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
Winson Chungc208ff92012-03-29 17:37:41 -07003241
Joe Onorato56d82912010-03-07 14:32:10 -05003242 // the db
3243 if (icon == null) {
3244 if (c != null) {
Michael Jurka931dc972011-08-05 15:08:15 -07003245 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05003246 }
3247 }
3248 // the fallback icon
3249 if (icon == null) {
Kenny Guyed131872014-04-30 03:02:21 +01003250 icon = mIconCache.getDefaultIcon(user);
Joe Onorato56d82912010-03-07 14:32:10 -05003251 info.usingFallbackIcon = true;
3252 }
3253 info.setIcon(icon);
3254
Kenny Guyed131872014-04-30 03:02:21 +01003255 // From the cache.
3256 if (labelCache != null) {
3257 info.title = labelCache.get(componentName);
3258 }
3259
Joe Onorato56d82912010-03-07 14:32:10 -05003260 // from the resource
Kenny Guyed131872014-04-30 03:02:21 +01003261 if (info.title == null && lai != null) {
3262 info.title = lai.getLabel();
3263 if (labelCache != null) {
3264 labelCache.put(componentName, info.title);
Winson Chungc3eecff2011-07-11 17:44:15 -07003265 }
Joe Onorato56d82912010-03-07 14:32:10 -05003266 }
3267 // from the db
Joe Onorato9c1289c2009-08-17 11:03:03 -04003268 if (info.title == null) {
Joe Onorato56d82912010-03-07 14:32:10 -05003269 if (c != null) {
3270 info.title = c.getString(titleIndex);
3271 }
3272 }
3273 // fall back to the class name of the activity
3274 if (info.title == null) {
3275 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003276 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003277 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
Kenny Guyed131872014-04-30 03:02:21 +01003278 info.user = user;
Kenny Guyc2bd8102014-06-30 12:30:31 +01003279 info.contentDescription = mUserManager.getBadgedLabelForUser(
3280 info.title.toString(), info.user);
Joe Onorato9c1289c2009-08-17 11:03:03 -04003281 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003282 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07003283
Winson Chung64359a52013-07-08 17:17:08 -07003284 static ArrayList<ItemInfo> filterItemInfos(Collection<ItemInfo> infos,
3285 ItemInfoFilter f) {
3286 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
3287 for (ItemInfo i : infos) {
3288 if (i instanceof ShortcutInfo) {
3289 ShortcutInfo info = (ShortcutInfo) i;
Sunny Goyal34942622014-08-29 17:20:55 -07003290 ComponentName cn = info.getTargetComponent();
Winson Chung64359a52013-07-08 17:17:08 -07003291 if (cn != null && f.filterItem(null, info, cn)) {
3292 filtered.add(info);
3293 }
3294 } else if (i instanceof FolderInfo) {
3295 FolderInfo info = (FolderInfo) i;
3296 for (ShortcutInfo s : info.contents) {
Sunny Goyal34942622014-08-29 17:20:55 -07003297 ComponentName cn = s.getTargetComponent();
Winson Chung64359a52013-07-08 17:17:08 -07003298 if (cn != null && f.filterItem(info, s, cn)) {
3299 filtered.add(s);
Winson Chung8a435102012-08-30 17:16:53 -07003300 }
3301 }
Winson Chung64359a52013-07-08 17:17:08 -07003302 } else if (i instanceof LauncherAppWidgetInfo) {
3303 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
3304 ComponentName cn = info.providerName;
3305 if (cn != null && f.filterItem(null, info, cn)) {
3306 filtered.add(info);
3307 }
Winson Chung8a435102012-08-30 17:16:53 -07003308 }
3309 }
Winson Chung64359a52013-07-08 17:17:08 -07003310 return new ArrayList<ItemInfo>(filtered);
3311 }
3312
Kenny Guyed131872014-04-30 03:02:21 +01003313 private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
3314 final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07003315 ItemInfoFilter filter = new ItemInfoFilter() {
3316 @Override
3317 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01003318 if (info.user == null) {
3319 return cn.equals(cname);
3320 } else {
3321 return cn.equals(cname) && info.user.equals(user);
3322 }
Winson Chung64359a52013-07-08 17:17:08 -07003323 }
3324 };
3325 return filterItemInfos(sBgItemsIdMap.values(), filter);
3326 }
3327
3328 public static boolean isShortcutInfoUpdateable(ItemInfo i) {
3329 if (i instanceof ShortcutInfo) {
3330 ShortcutInfo info = (ShortcutInfo) i;
3331 // We need to check for ACTION_MAIN otherwise getComponent() might
3332 // return null for some shortcuts (for instance, for shortcuts to
3333 // web pages.)
3334 Intent intent = info.intent;
3335 ComponentName name = intent.getComponent();
3336 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
3337 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3338 return true;
3339 }
Chris Wrenb6d4c282014-01-27 14:17:08 -05003340 // placeholder shortcuts get special treatment, let them through too.
Sunny Goyal34942622014-08-29 17:20:55 -07003341 if (info.isPromise()) {
Chris Wrenb6d4c282014-01-27 14:17:08 -05003342 return true;
3343 }
Winson Chung64359a52013-07-08 17:17:08 -07003344 }
3345 return false;
Winson Chung8a435102012-08-30 17:16:53 -07003346 }
3347
3348 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08003349 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003350 */
Joe Onorato0589f0f2010-02-08 13:44:00 -08003351 private ShortcutInfo getShortcutInfo(Cursor c, Context context,
Joe Onorato56d82912010-03-07 14:32:10 -05003352 int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
3353 int titleIndex) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003354
Joe Onorato56d82912010-03-07 14:32:10 -05003355 Bitmap icon = null;
Michael Jurkac9d95c52011-08-29 14:03:34 -07003356 final ShortcutInfo info = new ShortcutInfo();
Kenny Guyed131872014-04-30 03:02:21 +01003357 // Non-app shortcuts are only supported for current user.
3358 info.user = UserHandleCompat.myUserHandle();
Joe Onorato9c1289c2009-08-17 11:03:03 -04003359 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003360
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07003361 // TODO: If there's an explicit component and we can't install that, delete it.
3362
Joe Onorato56d82912010-03-07 14:32:10 -05003363 info.title = c.getString(titleIndex);
3364
Joe Onorato9c1289c2009-08-17 11:03:03 -04003365 int iconType = c.getInt(iconTypeIndex);
3366 switch (iconType) {
3367 case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
3368 String packageName = c.getString(iconPackageIndex);
3369 String resourceName = c.getString(iconResourceIndex);
3370 PackageManager packageManager = context.getPackageManager();
Joe Onorato56d82912010-03-07 14:32:10 -05003371 info.customIcon = false;
3372 // the resource
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003373 try {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003374 Resources resources = packageManager.getResourcesForApplication(packageName);
Joe Onorato56d82912010-03-07 14:32:10 -05003375 if (resources != null) {
3376 final int id = resources.getIdentifier(resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07003377 icon = Utilities.createIconBitmap(
3378 mIconCache.getFullResIcon(resources, id), context);
Joe Onorato56d82912010-03-07 14:32:10 -05003379 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003380 } catch (Exception e) {
Joe Onorato56d82912010-03-07 14:32:10 -05003381 // drop this. we have other places to look for icons
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003382 }
Joe Onorato56d82912010-03-07 14:32:10 -05003383 // the db
3384 if (icon == null) {
Michael Jurka931dc972011-08-05 15:08:15 -07003385 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05003386 }
3387 // the fallback icon
3388 if (icon == null) {
Kenny Guyed131872014-04-30 03:02:21 +01003389 icon = mIconCache.getDefaultIcon(info.user);
Joe Onorato56d82912010-03-07 14:32:10 -05003390 info.usingFallbackIcon = true;
3391 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003392 break;
3393 case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
Michael Jurka931dc972011-08-05 15:08:15 -07003394 icon = getIconFromCursor(c, iconIndex, context);
Joe Onorato56d82912010-03-07 14:32:10 -05003395 if (icon == null) {
Kenny Guyed131872014-04-30 03:02:21 +01003396 icon = mIconCache.getDefaultIcon(info.user);
Joe Onorato56d82912010-03-07 14:32:10 -05003397 info.customIcon = false;
3398 info.usingFallbackIcon = true;
3399 } else {
3400 info.customIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04003401 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003402 break;
3403 default:
Kenny Guyed131872014-04-30 03:02:21 +01003404 icon = mIconCache.getDefaultIcon(info.user);
Joe Onorato56d82912010-03-07 14:32:10 -05003405 info.usingFallbackIcon = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04003406 info.customIcon = false;
3407 break;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003408 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08003409 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04003410 return info;
3411 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003412
Michael Jurka931dc972011-08-05 15:08:15 -07003413 Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
Michael Jurka3a9fced2012-04-13 14:44:29 -07003414 @SuppressWarnings("all") // suppress dead code warning
3415 final boolean debug = false;
3416 if (debug) {
Joe Onorato56d82912010-03-07 14:32:10 -05003417 Log.d(TAG, "getIconFromCursor app="
3418 + c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
3419 }
3420 byte[] data = c.getBlob(iconIndex);
3421 try {
Michael Jurka931dc972011-08-05 15:08:15 -07003422 return Utilities.createIconBitmap(
3423 BitmapFactory.decodeByteArray(data, 0, data.length), context);
Joe Onorato56d82912010-03-07 14:32:10 -05003424 } catch (Exception e) {
3425 return null;
3426 }
3427 }
3428
Winson Chung3d503fb2011-07-13 17:25:49 -07003429 ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
3430 int cellX, int cellY, boolean notify) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07003431 final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
Adam Cohend9198822011-11-22 16:42:47 -08003432 if (info == null) {
3433 return null;
3434 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003435 addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003436
3437 return info;
3438 }
3439
Winson Chunga9abd0e2010-10-27 17:18:37 -07003440 /**
Winson Chung55cef262010-10-28 14:14:18 -07003441 * Attempts to find an AppWidgetProviderInfo that matches the given component.
3442 */
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003443 static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
Winson Chung55cef262010-10-28 14:14:18 -07003444 ComponentName component) {
3445 List<AppWidgetProviderInfo> widgets =
3446 AppWidgetManager.getInstance(context).getInstalledProviders();
3447 for (AppWidgetProviderInfo info : widgets) {
3448 if (info.provider.equals(component)) {
3449 return info;
3450 }
3451 }
3452 return null;
Winson Chunga9abd0e2010-10-27 17:18:37 -07003453 }
3454
3455 ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003456 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3457 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3458 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3459
Adam Cohend9198822011-11-22 16:42:47 -08003460 if (intent == null) {
3461 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3462 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3463 return null;
3464 }
3465
Joe Onorato0589f0f2010-02-08 13:44:00 -08003466 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003467 boolean customIcon = false;
3468 ShortcutIconResource iconResource = null;
3469
3470 if (bitmap != null && bitmap instanceof Bitmap) {
3471 icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003472 customIcon = true;
3473 } else {
3474 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
3475 if (extra != null && extra instanceof ShortcutIconResource) {
3476 try {
3477 iconResource = (ShortcutIconResource) extra;
3478 final PackageManager packageManager = context.getPackageManager();
3479 Resources resources = packageManager.getResourcesForApplication(
3480 iconResource.packageName);
3481 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
Michael Jurkac9a96192010-11-01 11:52:08 -07003482 icon = Utilities.createIconBitmap(
Kenny Guyed131872014-04-30 03:02:21 +01003483 mIconCache.getFullResIcon(resources, id),
3484 context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003485 } catch (Exception e) {
3486 Log.w(TAG, "Could not load shortcut icon: " + extra);
3487 }
3488 }
3489 }
3490
Michael Jurkac9d95c52011-08-29 14:03:34 -07003491 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05003492
Kenny Guyed131872014-04-30 03:02:21 +01003493 // Only support intents for current user for now. Intents sent from other
3494 // users wouldn't get here without intent forwarding anyway.
3495 info.user = UserHandleCompat.myUserHandle();
Joe Onorato56d82912010-03-07 14:32:10 -05003496 if (icon == null) {
Winson Chunga9abd0e2010-10-27 17:18:37 -07003497 if (fallbackIcon != null) {
3498 icon = fallbackIcon;
3499 } else {
Kenny Guyed131872014-04-30 03:02:21 +01003500 icon = mIconCache.getDefaultIcon(info.user);
Winson Chunga9abd0e2010-10-27 17:18:37 -07003501 info.usingFallbackIcon = true;
3502 }
Joe Onorato56d82912010-03-07 14:32:10 -05003503 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08003504 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05003505
Joe Onorato0589f0f2010-02-08 13:44:00 -08003506 info.title = name;
Kenny Guyc2bd8102014-06-30 12:30:31 +01003507 info.contentDescription = mUserManager.getBadgedLabelForUser(
3508 info.title.toString(), info.user);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003509 info.intent = intent;
3510 info.customIcon = customIcon;
3511 info.iconResource = iconResource;
3512
3513 return info;
3514 }
3515
Winson Chungaac01e12011-08-17 10:37:13 -07003516 boolean queueIconToBeChecked(HashMap<Object, byte[]> cache, ShortcutInfo info, Cursor c,
3517 int iconIndex) {
Joe Onorato17a89222011-02-08 17:26:11 -08003518 // If apps can't be on SD, don't even bother.
Winson Chungee055712013-07-30 14:46:24 -07003519 if (!mAppsCanBeOnRemoveableStorage) {
Winson Chungaac01e12011-08-17 10:37:13 -07003520 return false;
Joe Onorato17a89222011-02-08 17:26:11 -08003521 }
Joe Onorato56d82912010-03-07 14:32:10 -05003522 // If this icon doesn't have a custom icon, check to see
3523 // what's stored in the DB, and if it doesn't match what
3524 // we're going to show, store what we are going to show back
3525 // into the DB. We do this so when we're loading, if the
3526 // package manager can't find an icon (for example because
3527 // the app is on SD) then we can use that instead.
Joe Onoratoddc9c1f2010-08-30 18:30:15 -07003528 if (!info.customIcon && !info.usingFallbackIcon) {
Winson Chungaac01e12011-08-17 10:37:13 -07003529 cache.put(info, c.getBlob(iconIndex));
3530 return true;
3531 }
3532 return false;
3533 }
3534 void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
3535 boolean needSave = false;
3536 try {
3537 if (data != null) {
3538 Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
3539 Bitmap loaded = info.getIcon(mIconCache);
3540 needSave = !saved.sameAs(loaded);
3541 } else {
Joe Onorato56d82912010-03-07 14:32:10 -05003542 needSave = true;
3543 }
Winson Chungaac01e12011-08-17 10:37:13 -07003544 } catch (Exception e) {
3545 needSave = true;
3546 }
3547 if (needSave) {
3548 Log.d(TAG, "going to save icon bitmap for info=" + info);
3549 // This is slower than is ideal, but this only happens once
3550 // or when the app is updated with a new icon.
3551 updateItemInDatabase(context, info);
Joe Onorato56d82912010-03-07 14:32:10 -05003552 }
3553 }
3554
Joe Onorato9c1289c2009-08-17 11:03:03 -04003555 /**
Adam Cohendf2cc412011-04-27 16:56:57 -07003556 * Return an existing FolderInfo object if we have encountered this ID previously,
Joe Onorato9c1289c2009-08-17 11:03:03 -04003557 * or make a new one.
3558 */
Adam Cohendf2cc412011-04-27 16:56:57 -07003559 private static FolderInfo findOrMakeFolder(HashMap<Long, FolderInfo> folders, long id) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003560 // See if a placeholder was created for us already
3561 FolderInfo folderInfo = folders.get(id);
Adam Cohendf2cc412011-04-27 16:56:57 -07003562 if (folderInfo == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003563 // No placeholder -- create a new instance
Michael Jurkac9d95c52011-08-29 14:03:34 -07003564 folderInfo = new FolderInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04003565 folders.put(id, folderInfo);
3566 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003567 return folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003568 }
3569
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003570 public static final Comparator<AppInfo> getAppNameComparator() {
Winson Chung11904872012-09-17 16:58:46 -07003571 final Collator collator = Collator.getInstance();
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003572 return new Comparator<AppInfo>() {
3573 public final int compare(AppInfo a, AppInfo b) {
Kenny Guyed131872014-04-30 03:02:21 +01003574 if (a.user.equals(b.user)) {
3575 int result = collator.compare(a.title.toString().trim(),
3576 b.title.toString().trim());
3577 if (result == 0) {
3578 result = a.componentName.compareTo(b.componentName);
3579 }
3580 return result;
3581 } else {
3582 // TODO Need to figure out rules for sorting
3583 // profiles, this puts work second.
3584 return a.user.toString().compareTo(b.user.toString());
Winson Chung11904872012-09-17 16:58:46 -07003585 }
Michael Jurka5b1808d2011-07-11 19:59:46 -07003586 }
Winson Chung11904872012-09-17 16:58:46 -07003587 };
3588 }
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003589 public static final Comparator<AppInfo> APP_INSTALL_TIME_COMPARATOR
3590 = new Comparator<AppInfo>() {
3591 public final int compare(AppInfo a, AppInfo b) {
Winson Chung78403fe2011-01-21 15:38:02 -08003592 if (a.firstInstallTime < b.firstInstallTime) return 1;
3593 if (a.firstInstallTime > b.firstInstallTime) return -1;
3594 return 0;
3595 }
3596 };
Winson Chung5308f242011-08-18 12:12:41 -07003597 static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
3598 if (info.activityInfo != null) {
3599 return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
3600 } else {
3601 return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
3602 }
3603 }
Kenny Guyed131872014-04-30 03:02:21 +01003604 public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
Winson Chung11904872012-09-17 16:58:46 -07003605 private Collator mCollator;
Winson Chungc3eecff2011-07-11 17:44:15 -07003606 private HashMap<Object, CharSequence> mLabelCache;
Winson Chung785d2eb2011-04-14 16:08:02 -07003607 ShortcutNameComparator(PackageManager pm) {
Winson Chungc3eecff2011-07-11 17:44:15 -07003608 mLabelCache = new HashMap<Object, CharSequence>();
Winson Chung11904872012-09-17 16:58:46 -07003609 mCollator = Collator.getInstance();
Winson Chungc3eecff2011-07-11 17:44:15 -07003610 }
Kenny Guyed131872014-04-30 03:02:21 +01003611 ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
Winson Chungc3eecff2011-07-11 17:44:15 -07003612 mLabelCache = labelCache;
Winson Chung11904872012-09-17 16:58:46 -07003613 mCollator = Collator.getInstance();
Winson Chung785d2eb2011-04-14 16:08:02 -07003614 }
Kenny Guyed131872014-04-30 03:02:21 +01003615 public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
Sunny Goyal0c4a6442014-07-22 12:27:04 -07003616 String labelA, labelB;
Kenny Guyed131872014-04-30 03:02:21 +01003617 ComponentName keyA = a.getComponentName();
3618 ComponentName keyB = b.getComponentName();
Winson Chung5308f242011-08-18 12:12:41 -07003619 if (mLabelCache.containsKey(keyA)) {
Sunny Goyal0c4a6442014-07-22 12:27:04 -07003620 labelA = mLabelCache.get(keyA).toString();
Winson Chungc3eecff2011-07-11 17:44:15 -07003621 } else {
Kenny Guyed131872014-04-30 03:02:21 +01003622 labelA = a.getLabel().toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003623
Winson Chung5308f242011-08-18 12:12:41 -07003624 mLabelCache.put(keyA, labelA);
Winson Chungc3eecff2011-07-11 17:44:15 -07003625 }
Winson Chung5308f242011-08-18 12:12:41 -07003626 if (mLabelCache.containsKey(keyB)) {
Sunny Goyal0c4a6442014-07-22 12:27:04 -07003627 labelB = mLabelCache.get(keyB).toString();
Winson Chungc3eecff2011-07-11 17:44:15 -07003628 } else {
Kenny Guyed131872014-04-30 03:02:21 +01003629 labelB = b.getLabel().toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003630
Winson Chung5308f242011-08-18 12:12:41 -07003631 mLabelCache.put(keyB, labelB);
Winson Chungc3eecff2011-07-11 17:44:15 -07003632 }
Winson Chung11904872012-09-17 16:58:46 -07003633 return mCollator.compare(labelA, labelB);
Winson Chung785d2eb2011-04-14 16:08:02 -07003634 }
3635 };
Winson Chung1ed747a2011-05-03 16:18:34 -07003636 public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
Sunny Goyalffe83f12014-08-14 17:39:34 -07003637 private final AppWidgetManagerCompat mManager;
3638 private final PackageManager mPackageManager;
3639 private final HashMap<Object, String> mLabelCache;
3640 private final Collator mCollator;
3641
3642 WidgetAndShortcutNameComparator(Context context) {
3643 mManager = AppWidgetManagerCompat.getInstance(context);
3644 mPackageManager = context.getPackageManager();
Winson Chung1ed747a2011-05-03 16:18:34 -07003645 mLabelCache = new HashMap<Object, String>();
Winson Chung11904872012-09-17 16:58:46 -07003646 mCollator = Collator.getInstance();
Winson Chung1ed747a2011-05-03 16:18:34 -07003647 }
3648 public final int compare(Object a, Object b) {
3649 String labelA, labelB;
Winson Chungc3eecff2011-07-11 17:44:15 -07003650 if (mLabelCache.containsKey(a)) {
3651 labelA = mLabelCache.get(a);
3652 } else {
Sunny Goyalffe83f12014-08-14 17:39:34 -07003653 labelA = (a instanceof AppWidgetProviderInfo)
3654 ? mManager.loadLabel((AppWidgetProviderInfo) a)
3655 : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003656 mLabelCache.put(a, labelA);
3657 }
3658 if (mLabelCache.containsKey(b)) {
3659 labelB = mLabelCache.get(b);
3660 } else {
Sunny Goyalffe83f12014-08-14 17:39:34 -07003661 labelB = (b instanceof AppWidgetProviderInfo)
3662 ? mManager.loadLabel((AppWidgetProviderInfo) b)
3663 : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
Winson Chungc3eecff2011-07-11 17:44:15 -07003664 mLabelCache.put(b, labelB);
3665 }
Winson Chung11904872012-09-17 16:58:46 -07003666 return mCollator.compare(labelA, labelB);
Winson Chung1ed747a2011-05-03 16:18:34 -07003667 }
3668 };
Joe Onoratobe386092009-11-17 17:32:16 -08003669
Sunny Goyal651077b2014-06-30 14:15:31 -07003670 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3671 return (provider != null) && (provider.provider != null)
3672 && (provider.provider.getPackageName() != null);
3673 }
3674
Joe Onoratobe386092009-11-17 17:32:16 -08003675 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08003676 Log.d(TAG, "mCallbacks=" + mCallbacks);
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003677 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3678 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3679 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3680 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
Joe Onorato36115782010-06-17 13:28:48 -04003681 if (mLoaderTask != null) {
3682 mLoaderTask.dumpState();
3683 } else {
3684 Log.d(TAG, "mLoaderTask=null");
3685 }
Joe Onoratobe386092009-11-17 17:32:16 -08003686 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003687}