blob: 3d31b4eb2625d69fade642a6a1ba6c9a82f5f5ca [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.AppWidgetProviderInfo;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070021import android.content.BroadcastReceiver;
22import android.content.ComponentName;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070023import android.content.ContentProviderOperation;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Context;
27import android.content.Intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -080028import android.content.Intent.ShortcutIconResource;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070029import android.content.IntentFilter;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.pm.PackageManager;
Jason Monkbbe1e242014-05-16 17:37:34 -040031import android.content.pm.ProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.content.pm.ResolveInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.database.Cursor;
34import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.net.Uri;
Joe Onorato36115782010-06-17 13:28:48 -040036import android.os.Handler;
37import android.os.HandlerThread;
Sunny Goyal756adbc2015-04-16 15:20:51 -070038import android.os.Looper;
Joe Onorato0589f0f2010-02-08 13:44:00 -080039import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040import android.os.Process;
Joe Onorato9c1289c2009-08-17 11:03:03 -040041import android.os.SystemClock;
Chris Wrenc3919c02013-09-18 09:48:33 -040042import android.provider.BaseColumns;
Winson Chunga90303b2013-11-15 13:05:06 -080043import android.text.TextUtils;
Winson Chungaafa03c2010-06-11 17:34:16 -070044import android.util.Log;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -080045import android.util.LongSparseArray;
Winson Chungc9168342013-06-26 14:54:55 -070046import android.util.Pair;
Michael Jurka34c2e6c2013-12-13 16:07:45 +010047
Sunny Goyalffe83f12014-08-14 17:39:34 -070048import com.android.launcher3.compat.AppWidgetManagerCompat;
Kenny Guyed131872014-04-30 03:02:21 +010049import com.android.launcher3.compat.LauncherActivityInfoCompat;
50import com.android.launcher3.compat.LauncherAppsCompat;
Sunny Goyal34942622014-08-29 17:20:55 -070051import com.android.launcher3.compat.PackageInstallerCompat;
Sunny Goyale755d462014-07-22 13:48:29 -070052import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
Kenny Guyed131872014-04-30 03:02:21 +010053import com.android.launcher3.compat.UserHandleCompat;
54import com.android.launcher3.compat.UserManagerCompat;
Sunny Goyal6c56c682015-07-16 14:09:05 -070055import com.android.launcher3.config.ProviderConfig;
Tony Wickham827cef22016-03-17 15:39:39 -070056import com.android.launcher3.dynamicui.ExtractionUtils;
Sunny Goyal26119432016-02-18 22:09:23 +000057import com.android.launcher3.folder.Folder;
58import com.android.launcher3.folder.FolderIcon;
Sunny Goyalf862a262015-12-14 14:27:38 -080059import com.android.launcher3.model.GridSizeMigrationTask;
Hyunyoung Song2bd3d7d2015-05-21 13:04:53 -070060import com.android.launcher3.model.WidgetsModel;
Robin Lee26ace122015-03-16 19:41:43 +000061import com.android.launcher3.util.ComponentKey;
Sunny Goyal4e5cc642015-06-25 16:37:44 -070062import com.android.launcher3.util.CursorIconInfo;
Sunny Goyal713edfc2016-05-06 09:58:34 -070063import com.android.launcher3.logging.FileLog;
Sunny Goyal3bbbabc2016-03-15 09:16:30 -070064import com.android.launcher3.util.FlagOp;
Sunny Goyale2df0622015-04-24 11:27:00 -070065import com.android.launcher3.util.LongArrayMap;
Sunny Goyal18bf8e22015-04-08 18:13:46 -070066import com.android.launcher3.util.ManagedProfileHeuristic;
Sunny Goyald09c3702016-04-06 16:18:20 -070067import com.android.launcher3.util.PackageManagerHelper;
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -070068import com.android.launcher3.util.Preconditions;
Sunny Goyalda891c12016-03-18 18:29:24 -070069import com.android.launcher3.util.StringFilter;
Adam Cohen091440a2015-03-18 14:16:05 -070070import com.android.launcher3.util.Thunk;
Sunny Goyal527c7d32015-08-28 15:19:36 -070071import com.android.launcher3.util.ViewOnDrawExecutor;
Romain Guyedcce092010-03-04 13:03:17 -080072
Michael Jurkac2f801e2011-07-12 14:19:46 -070073import java.lang.ref.WeakReference;
74import java.net.URISyntaxException;
Sunny Goyal34942622014-08-29 17:20:55 -070075import java.security.InvalidParameterException;
Michael Jurkac2f801e2011-07-12 14:19:46 -070076import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070077import java.util.Arrays;
Michael Jurkac2f801e2011-07-12 14:19:46 -070078import java.util.Collections;
79import java.util.Comparator;
80import java.util.HashMap;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070081import java.util.HashSet;
Winson Chung2abf94d2012-07-18 18:16:38 -070082import java.util.Iterator;
Michael Jurkac2f801e2011-07-12 14:19:46 -070083import java.util.List;
Sunny Goyalf599ccf2014-07-08 13:01:29 -070084import java.util.Map.Entry;
Winson Chungb8b2a5a2012-07-12 17:55:31 -070085import java.util.Set;
Sunny Goyal527c7d32015-08-28 15:19:36 -070086import java.util.concurrent.Executor;
Michael Jurkac2f801e2011-07-12 14:19:46 -070087
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088/**
89 * Maintains in-memory state of the Launcher. It is expected that there should be only one
90 * LauncherModel object held in a static. Also provide APIs for updating the database state
The Android Open Source Projectbc219c32009-03-09 11:52:14 -070091 * for the Launcher.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080092 */
Kenny Guyed131872014-04-30 03:02:21 +010093public class LauncherModel extends BroadcastReceiver
Kenny Guyc2bd8102014-06-30 12:30:31 +010094 implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080095 static final boolean DEBUG_LOADERS = false;
Chris Wrenee523362014-09-09 10:09:02 -040096 private static final boolean DEBUG_RECEIVER = false;
Sunny Goyal94485362014-09-18 16:13:58 -070097 private static final boolean REMOVE_UNRESTORED_ICONS = true;
Chris Wrenb358f812014-04-16 13:37:00 -040098
Joe Onorato9c1289c2009-08-17 11:03:03 -040099 static final String TAG = "Launcher.Model";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700100
Dan Sandlerd5024042014-01-09 15:01:33 -0500101 public static final int LOADER_FLAG_NONE = 0;
102 public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
103 public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
104
Joe Onorato36115782010-06-17 13:28:48 -0400105 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
Derek Prothro7aff3992013-12-10 14:00:37 -0500106 private static final long INVALID_SCREEN_ID = -1L;
Winson Chunga6945242014-01-08 14:04:34 -0800107
Winson Chunga6945242014-01-08 14:04:34 -0800108 private final boolean mOldContentProviderExists;
Daniel Sandlerdca66122010-04-13 16:23:58 -0400109
Adam Cohen091440a2015-03-18 14:16:05 -0700110 @Thunk final LauncherAppState mApp;
111 @Thunk final Object mLock = new Object();
112 @Thunk DeferredHandler mHandler = new DeferredHandler();
113 @Thunk LoaderTask mLoaderTask;
114 @Thunk boolean mIsLoaderTaskRunning;
Sunny Goyal756a28a2015-04-23 17:07:55 -0700115 @Thunk boolean mHasLoaderCompletedOnce;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800116
Jason Monkbbe1e242014-05-16 17:37:34 -0400117 private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
Winson Chung81b52252012-08-27 15:34:29 -0700118
Adam Cohen091440a2015-03-18 14:16:05 -0700119 @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700120 static {
121 sWorkerThread.start();
122 }
Adam Cohen091440a2015-03-18 14:16:05 -0700123 @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
Brad Fitzpatrick700889f2010-10-11 09:40:44 -0700124
Joe Onoratocc67f472010-06-08 10:54:30 -0700125 // We start off with everything not loaded. After that, we assume that
126 // our monitoring of the package manager provides all updates and we never
127 // need to do a requery. These are only ever touched from the loader thread.
Adam Cohen091440a2015-03-18 14:16:05 -0700128 @Thunk boolean mWorkspaceLoaded;
129 @Thunk boolean mAllAppsLoaded;
Joe Onoratocc67f472010-06-08 10:54:30 -0700130
Sunny Goyal756a28a2015-04-23 17:07:55 -0700131 /**
132 * Set of runnables to be called on the background thread after the workspace binding
133 * is complete.
134 */
135 static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>();
136
Adam Cohen091440a2015-03-18 14:16:05 -0700137 @Thunk WeakReference<Callbacks> mCallbacks;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800138
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700139 // < only access in worker thread >
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800140 private final AllAppsList mBgAllAppsList;
Hyunyoung Song9110d482015-05-22 14:49:23 -0700141 // Entire list of widgets.
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800142 private final WidgetsModel mBgWidgetsModel;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800143
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700144 // The lock that must be acquired before referencing any static bg data structures. Unlike
145 // other locks, this one can generally be held long-term because we never expect any of these
146 // static data structures to be referenced outside of the worker thread except on the first
147 // load after configuration change.
Winson Chung2abf94d2012-07-18 18:16:38 -0700148 static final Object sBgLock = new Object();
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700149
Adam Cohen487f7dd2012-06-28 18:12:10 -0700150 // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700151 // LauncherModel to their ids
Sunny Goyale2df0622015-04-24 11:27:00 -0700152 static final LongArrayMap<ItemInfo> sBgItemsIdMap = new LongArrayMap<>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700153
Adam Cohen487f7dd2012-06-28 18:12:10 -0700154 // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
155 // created by LauncherModel that are directly on the home screen (however, no widgets or
156 // shortcuts within folders).
157 static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700158
Adam Cohen487f7dd2012-06-28 18:12:10 -0700159 // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
160 static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700161 new ArrayList<LauncherAppWidgetInfo>();
162
Adam Cohen487f7dd2012-06-28 18:12:10 -0700163 // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
Sunny Goyale2df0622015-04-24 11:27:00 -0700164 static final LongArrayMap<FolderInfo> sBgFolders = new LongArrayMap<>();
Winson Chungb1094bd2011-08-24 16:14:08 -0700165
Adam Cohendcd297f2013-06-18 13:13:40 -0700166 // sBgWorkspaceScreens is the ordered set of workspace screens.
167 static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
168
Sunny Goyalf599ccf2014-07-08 13:01:29 -0700169 // sPendingPackages is a set of packages which could be on sdcard and are not available yet
Sameer Padala513edae2014-07-29 16:17:08 -0700170 static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
171 new HashMap<UserHandleCompat, HashSet<String>>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -0700172
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700173 // </ only access in worker thread >
174
Adam Cohen091440a2015-03-18 14:16:05 -0700175 @Thunk IconCache mIconCache;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800176
Adam Cohen091440a2015-03-18 14:16:05 -0700177 @Thunk final LauncherAppsCompat mLauncherApps;
178 @Thunk final UserManagerCompat mUserManager;
Kenny Guyed131872014-04-30 03:02:21 +0100179
Joe Onorato9c1289c2009-08-17 11:03:03 -0400180 public interface Callbacks {
Joe Onoratoef2efcf2010-10-27 13:21:00 -0700181 public boolean setLoadOnResume();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400182 public int getCurrentWorkspaceScreen();
Sunny Goyal527c7d32015-08-28 15:19:36 -0700183 public void clearPendingBinds();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400184 public void startBinding();
Winson Chung64359a52013-07-08 17:17:08 -0700185 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
186 boolean forceAnimateIcons);
Adam Cohendcd297f2013-06-18 13:13:40 -0700187 public void bindScreens(ArrayList<Long> orderedScreenIds);
Winson Chung64359a52013-07-08 17:17:08 -0700188 public void bindAddScreens(ArrayList<Long> orderedScreenIds);
Sunny Goyal66cfdc22015-02-02 13:01:51 -0800189 public void finishBindingItems();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400190 public void bindAppWidget(LauncherAppWidgetInfo info);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200191 public void bindAllApplications(ArrayList<AppInfo> apps);
Winson Chungd64d1762013-08-20 14:37:16 -0700192 public void bindAppsAdded(ArrayList<Long> newScreens,
193 ArrayList<ItemInfo> addNotAnimated,
Winson Chungc58497e2013-09-03 17:48:37 -0700194 ArrayList<ItemInfo> addAnimated,
195 ArrayList<AppInfo> addedApps);
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200196 public void bindAppsUpdated(ArrayList<AppInfo> apps);
Sunny Goyal4390ace2014-10-13 11:33:11 -0700197 public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
198 ArrayList<ShortcutInfo> removed, UserHandleCompat user);
199 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
Sunny Goyal756adbc2015-04-16 15:20:51 -0700200 public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
Sunny Goyal3bbbabc2016-03-15 09:16:30 -0700201 public void bindWorkspaceComponentsRemoved(
202 HashSet<String> packageNames, HashSet<ComponentName> components,
203 UserHandleCompat user);
204 public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
Sunny Goyal2e1efb42016-03-03 16:58:55 -0800205 public void notifyWidgetProvidersChanged();
206 public void bindWidgetsModel(WidgetsModel model);
Winson Chung88fa7412015-08-03 14:25:28 -0700207 public void bindSearchProviderChanged();
Winson Chunga0b7e862013-09-05 16:03:15 -0700208 public boolean isAllAppsButtonRank(int rank);
Adam Cohen1462de32012-07-24 22:34:36 -0700209 public void onPageBoundSynchronously(int page);
Sunny Goyal527c7d32015-08-28 15:19:36 -0700210 public void executeOnNextDraw(ViewOnDrawExecutor executor);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400211 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212
Winson Chung64359a52013-07-08 17:17:08 -0700213 public interface ItemInfoFilter {
214 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
215 }
216
Bjorn Bringert1307f632013-10-03 22:31:03 +0100217 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Winson Chunga6945242014-01-08 14:04:34 -0800218 Context context = app.getContext();
Daniel Sandlere4f98912013-06-25 15:13:26 -0400219
Adam Cohen71483f42014-05-15 14:04:01 -0700220 String oldProvider = context.getString(R.string.old_launcher_provider_uri);
Jason Monkbbe1e242014-05-16 17:37:34 -0400221 // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
222 // resource string.
223 String redirectAuthority = Uri.parse(oldProvider).getAuthority();
224 ProviderInfo providerInfo =
225 context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
226 ProviderInfo redirectProvider =
227 context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
Adam Cohen71483f42014-05-15 14:04:01 -0700228
229 Log.d(TAG, "Old launcher provider: " + oldProvider);
Jason Monkbbe1e242014-05-16 17:37:34 -0400230 mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
Adam Cohen71483f42014-05-15 14:04:01 -0700231
232 if (mOldContentProviderExists) {
233 Log.d(TAG, "Old launcher provider exists.");
234 } else {
235 Log.d(TAG, "Old launcher provider does not exist.");
236 }
237
Daniel Sandlere4f98912013-06-25 15:13:26 -0400238 mApp = app;
Bjorn Bringert1307f632013-10-03 22:31:03 +0100239 mBgAllAppsList = new AllAppsList(iconCache, appFilter);
Hyunyoung Songeaf291b2015-06-17 21:12:44 -0700240 mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800241 mIconCache = iconCache;
242
Kenny Guyed131872014-04-30 03:02:21 +0100243 mLauncherApps = LauncherAppsCompat.getInstance(context);
244 mUserManager = UserManagerCompat.getInstance(context);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800245 }
246
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700247 /** Runs the specified runnable immediately if called from the main thread, otherwise it is
248 * posted on the main thread handler. */
Adam Cohen091440a2015-03-18 14:16:05 -0700249 @Thunk void runOnMainThread(Runnable r) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700250 if (sWorkerThread.getThreadId() == Process.myTid()) {
251 // If we are on the worker thread, post onto the main handler
252 mHandler.post(r);
253 } else {
254 r.run();
255 }
256 }
257
258 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
259 * posted on the worker thread handler. */
Sunny Goyal639e9062015-08-19 19:17:06 -0700260 @Thunk static void runOnWorkerThread(Runnable r) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700261 if (sWorkerThread.getThreadId() == Process.myTid()) {
262 r.run();
263 } else {
264 // If we are not on the worker thread, then post to the worker handler
265 sWorker.post(r);
266 }
267 }
268
Winson Chunge43a1e72014-01-15 10:33:02 -0800269 boolean canMigrateFromOldLauncherDb(Launcher launcher) {
270 return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
Winson Chunga6945242014-01-08 14:04:34 -0800271 }
272
Sunny Goyal756adbc2015-04-16 15:20:51 -0700273 public void setPackageState(final PackageInstallInfo installInfo) {
274 Runnable updateRunnable = new Runnable() {
275
276 @Override
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500277 public void run() {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700278 synchronized (sBgLock) {
279 final HashSet<ItemInfo> updates = new HashSet<>();
280
281 if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
282 // Ignore install success events as they are handled by Package add events.
283 return;
284 }
285
Sunny Goyale2df0622015-04-24 11:27:00 -0700286 for (ItemInfo info : sBgItemsIdMap) {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700287 if (info instanceof ShortcutInfo) {
288 ShortcutInfo si = (ShortcutInfo) info;
289 ComponentName cn = si.getTargetComponent();
290 if (si.isPromise() && (cn != null)
291 && installInfo.packageName.equals(cn.getPackageName())) {
292 si.setInstallProgress(installInfo.progress);
293
294 if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
295 // Mark this info as broken.
296 si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
297 }
298 updates.add(si);
299 }
300 }
301 }
302
303 for (LauncherAppWidgetInfo widget : sBgAppWidgets) {
304 if (widget.providerName.getPackageName().equals(installInfo.packageName)) {
305 widget.installProgress = installInfo.progress;
306 updates.add(widget);
307 }
308 }
309
310 if (!updates.isEmpty()) {
311 // Push changes to the callback.
312 Runnable r = new Runnable() {
313 public void run() {
314 Callbacks callbacks = getCallback();
315 if (callbacks != null) {
316 callbacks.bindRestoreItemsChange(updates);
317 }
318 }
319 };
320 mHandler.post(r);
321 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500322 }
323 }
324 };
Sunny Goyal756adbc2015-04-16 15:20:51 -0700325 runOnWorkerThread(updateRunnable);
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500326 }
327
Sunny Goyal756adbc2015-04-16 15:20:51 -0700328 /**
329 * Updates the icons and label of all pending icons for the provided package name.
330 */
331 public void updateSessionDisplayInfo(final String packageName) {
332 Runnable updateRunnable = new Runnable() {
333
334 @Override
Sunny Goyala22666f2014-09-18 13:25:15 -0700335 public void run() {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700336 synchronized (sBgLock) {
337 final ArrayList<ShortcutInfo> updates = new ArrayList<>();
338 final UserHandleCompat user = UserHandleCompat.myUserHandle();
339
Sunny Goyale2df0622015-04-24 11:27:00 -0700340 for (ItemInfo info : sBgItemsIdMap) {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700341 if (info instanceof ShortcutInfo) {
342 ShortcutInfo si = (ShortcutInfo) info;
343 ComponentName cn = si.getTargetComponent();
344 if (si.isPromise() && (cn != null)
345 && packageName.equals(cn.getPackageName())) {
346 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
347 // For auto install apps update the icon as well as label.
348 mIconCache.getTitleAndIcon(si,
349 si.promisedIntent, user,
350 si.shouldUseLowResIcon());
351 } else {
352 // Only update the icon for restored apps.
353 si.updateIcon(mIconCache);
354 }
355 updates.add(si);
356 }
357 }
358 }
359
360 if (!updates.isEmpty()) {
361 // Push changes to the callback.
362 Runnable r = new Runnable() {
363 public void run() {
364 Callbacks callbacks = getCallback();
365 if (callbacks != null) {
366 callbacks.bindShortcutsChanged(updates,
367 new ArrayList<ShortcutInfo>(), user);
368 }
369 }
370 };
371 mHandler.post(r);
372 }
Sunny Goyala22666f2014-09-18 13:25:15 -0700373 }
374 }
375 };
Sunny Goyal756adbc2015-04-16 15:20:51 -0700376 runOnWorkerThread(updateRunnable);
Sunny Goyala22666f2014-09-18 13:25:15 -0700377 }
378
Adam Cohen76a47a12014-02-05 11:47:43 -0800379 public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
Sunny Goyale0f58d72014-11-10 18:05:31 -0800380 final Callbacks callbacks = getCallback();
Adam Cohen76a47a12014-02-05 11:47:43 -0800381
382 if (allAppsApps == null) {
383 throw new RuntimeException("allAppsApps must not be null");
384 }
385 if (allAppsApps.isEmpty()) {
386 return;
387 }
388
389 // Process the newly added applications and add them to the database first
390 Runnable r = new Runnable() {
391 public void run() {
392 runOnMainThread(new Runnable() {
393 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -0800394 Callbacks cb = getCallback();
Adam Cohen76a47a12014-02-05 11:47:43 -0800395 if (callbacks == cb && cb != null) {
Chris Wren6d0dde02014-02-10 12:16:54 -0500396 callbacks.bindAppsAdded(null, null, null, allAppsApps);
Adam Cohen76a47a12014-02-05 11:47:43 -0800397 }
398 }
399 });
400 }
401 };
402 runOnWorkerThread(r);
Winson Chung997a9232013-07-24 15:33:46 -0700403 }
Adam Cohen76a47a12014-02-05 11:47:43 -0800404
Sunny Goyala9116722015-04-29 13:55:58 -0700405 private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800406 int[] xy, int spanX, int spanY) {
407 LauncherAppState app = LauncherAppState.getInstance();
Adam Cohen2e6da152015-05-06 11:42:25 -0700408 InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
409 final int xCount = (int) profile.numColumns;
410 final int yCount = (int) profile.numRows;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800411 boolean[][] occupied = new boolean[xCount][yCount];
412 if (occupiedPos != null) {
Sunny Goyala9116722015-04-29 13:55:58 -0700413 for (ItemInfo r : occupiedPos) {
414 int right = r.cellX + r.spanX;
415 int bottom = r.cellY + r.spanY;
416 for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
417 for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800418 occupied[x][y] = true;
419 }
420 }
421 }
Winson Chungfe9d96a2013-11-14 11:30:05 -0800422 }
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700423 return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800424 }
425
426 /**
427 * Find a position on the screen for the given size or adds a new screen.
428 * @return screenId and the coordinates for the item.
429 */
Sunny Goyal756a28a2015-04-23 17:07:55 -0700430 @Thunk Pair<Long, int[]> findSpaceForItem(
Sunny Goyalc3642d42015-10-30 10:27:08 -0700431 Context context,
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800432 ArrayList<Long> workspaceScreens,
433 ArrayList<Long> addedWorkspaceScreensFinal,
434 int spanX, int spanY) {
Sunny Goyala9116722015-04-29 13:55:58 -0700435 LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800436
Sunny Goyala9116722015-04-29 13:55:58 -0700437 // Use sBgItemsIdMap as all the items are already loaded.
Sunny Goyal756a28a2015-04-23 17:07:55 -0700438 assertWorkspaceLoaded();
Sunny Goyala9116722015-04-29 13:55:58 -0700439 synchronized (sBgLock) {
440 for (ItemInfo info : sBgItemsIdMap) {
441 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
442 ArrayList<ItemInfo> items = screenItems.get(info.screenId);
443 if (items == null) {
444 items = new ArrayList<>();
445 screenItems.put(info.screenId, items);
446 }
447 items.add(info);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800448 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800449 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800450 }
451
452 // Find appropriate space for the item.
453 long screenId = 0;
454 int[] cordinates = new int[2];
455 boolean found = false;
456
457 int screenCount = workspaceScreens.size();
458 // First check the preferred screen.
Sunny Goyala9116722015-04-29 13:55:58 -0700459 int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800460 if (preferredScreenIndex < screenCount) {
461 screenId = workspaceScreens.get(preferredScreenIndex);
462 found = findNextAvailableIconSpaceInScreen(
463 screenItems.get(screenId), cordinates, spanX, spanY);
464 }
465
466 if (!found) {
Sunny Goyala9116722015-04-29 13:55:58 -0700467 // Search on any of the screens starting from the first screen.
468 for (int screen = 1; screen < screenCount; screen++) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800469 screenId = workspaceScreens.get(screen);
470 if (findNextAvailableIconSpaceInScreen(
471 screenItems.get(screenId), cordinates, spanX, spanY)) {
472 // We found a space for it
473 found = true;
474 break;
475 }
476 }
477 }
478
479 if (!found) {
480 // Still no position found. Add a new screen to the end.
Sunny Goyald2497482015-09-22 18:24:19 -0700481 screenId = LauncherSettings.Settings.call(context.getContentResolver(),
482 LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
483 .getLong(LauncherSettings.Settings.EXTRA_VALUE);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800484
485 // Save the screen id for binding in the workspace
486 workspaceScreens.add(screenId);
487 addedWorkspaceScreensFinal.add(screenId);
488
489 // If we still can't find an empty space, then God help us all!!!
490 if (!findNextAvailableIconSpaceInScreen(
491 screenItems.get(screenId), cordinates, spanX, spanY)) {
492 throw new RuntimeException("Can't find space to add the item");
493 }
494 }
495 return Pair.create(screenId, cordinates);
496 }
497
498 /**
499 * Adds the provided items to the workspace.
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800500 */
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700501 public void addAndBindAddedWorkspaceItems(final Context context,
Sunny Goyala214a632015-05-06 12:23:34 -0700502 final ArrayList<? extends ItemInfo> workspaceApps) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800503 final Callbacks callbacks = getCallback();
Adam Cohen76a47a12014-02-05 11:47:43 -0800504 if (workspaceApps.isEmpty()) {
Winson Chung9e6a0a22013-08-27 11:58:12 -0700505 return;
Winson Chung997a9232013-07-24 15:33:46 -0700506 }
Winson Chung64359a52013-07-08 17:17:08 -0700507 // Process the newly added applications and add them to the database first
508 Runnable r = new Runnable() {
509 public void run() {
510 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
511 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
512
Winson Chung76828c82013-08-19 15:43:29 -0700513 // Get the list of workspace screens. We need to append to this list and
514 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
515 // called.
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -0800516 ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context);
Winson Chung64359a52013-07-08 17:17:08 -0700517 synchronized(sBgLock) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800518 for (ItemInfo item : workspaceApps) {
Sunny Goyala9116722015-04-29 13:55:58 -0700519 if (item instanceof ShortcutInfo) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800520 // Short-circuit this logic if the icon exists somewhere on the workspace
Sunny Goyal756adbc2015-04-16 15:20:51 -0700521 if (shortcutExists(context, item.getIntent(), item.user)) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800522 continue;
Winson Chungc763c4e2013-07-19 13:49:06 -0700523 }
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800524 }
Winson Chung76828c82013-08-19 15:43:29 -0700525
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800526 // Find appropriate space for the item.
Sunny Goyalc3642d42015-10-30 10:27:08 -0700527 Pair<Long, int[]> coords = findSpaceForItem(context,
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700528 workspaceScreens, addedWorkspaceScreensFinal, 1, 1);
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800529 long screenId = coords.first;
530 int[] cordinates = coords.second;
Winson Chung64359a52013-07-08 17:17:08 -0700531
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700532 ItemInfo itemInfo;
533 if (item instanceof ShortcutInfo || item instanceof FolderInfo) {
534 itemInfo = item;
Sunny Goyal71b5c0b2015-01-08 16:59:04 -0800535 } else if (item instanceof AppInfo) {
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700536 itemInfo = ((AppInfo) item).makeShortcut();
Winson Chung997a9232013-07-24 15:33:46 -0700537 } else {
538 throw new RuntimeException("Unexpected info type");
539 }
Winson Chung94d67682013-09-25 16:29:40 -0700540
Winson Chung64359a52013-07-08 17:17:08 -0700541 // Add the shortcut to the db
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700542 addItemToDatabase(context, itemInfo,
Winson Chung64359a52013-07-08 17:17:08 -0700543 LauncherSettings.Favorites.CONTAINER_DESKTOP,
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700544 screenId, cordinates[0], cordinates[1]);
Winson Chung64359a52013-07-08 17:17:08 -0700545 // Save the ShortcutInfo for binding in the workspace
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700546 addedShortcutsFinal.add(itemInfo);
Winson Chung64359a52013-07-08 17:17:08 -0700547 }
548 }
549
Winson Chung76828c82013-08-19 15:43:29 -0700550 // Update the workspace screens
551 updateWorkspaceScreenOrder(context, workspaceScreens);
552
Adam Cohen76a47a12014-02-05 11:47:43 -0800553 if (!addedShortcutsFinal.isEmpty()) {
Winson Chung997a9232013-07-24 15:33:46 -0700554 runOnMainThread(new Runnable() {
555 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -0800556 Callbacks cb = getCallback();
Winson Chung997a9232013-07-24 15:33:46 -0700557 if (callbacks == cb && cb != null) {
Winson Chung997a9232013-07-24 15:33:46 -0700558 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
559 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
Winson Chung94d67682013-09-25 16:29:40 -0700560 if (!addedShortcutsFinal.isEmpty()) {
561 ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
562 long lastScreenId = info.screenId;
563 for (ItemInfo i : addedShortcutsFinal) {
564 if (i.screenId == lastScreenId) {
565 addAnimated.add(i);
566 } else {
567 addNotAnimated.add(i);
568 }
Winson Chung997a9232013-07-24 15:33:46 -0700569 }
570 }
Winson Chungd64d1762013-08-20 14:37:16 -0700571 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
Adam Cohen76a47a12014-02-05 11:47:43 -0800572 addNotAnimated, addAnimated, null);
Winson Chung997a9232013-07-24 15:33:46 -0700573 }
Winson Chung64359a52013-07-08 17:17:08 -0700574 }
Winson Chung997a9232013-07-24 15:33:46 -0700575 });
576 }
Winson Chung64359a52013-07-08 17:17:08 -0700577 }
578 };
579 runOnWorkerThread(r);
580 }
581
Joe Onorato9c1289c2009-08-17 11:03:03 -0400582 /**
583 * Adds an item to the DB if it was not created previously, or move it to a new
584 * <container, screen, cellX, cellY>
585 */
Adam Cohenf9c184a2016-01-15 16:47:43 -0800586 public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700587 long screenId, int cellX, int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400588 if (item.container == ItemInfo.NO_ID) {
589 // From all apps
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700590 addItemToDatabase(context, item, container, screenId, cellX, cellY);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400591 } else {
592 // From somewhere else
Adam Cohendcd297f2013-06-18 13:13:40 -0700593 moveItemInDatabase(context, item, container, screenId, cellX, cellY);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800594 }
595 }
596
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700597 static void checkItemInfoLocked(
598 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
599 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
600 if (modelItem != null && item != modelItem) {
601 // check all the data is consistent
602 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
603 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
604 ShortcutInfo shortcut = (ShortcutInfo) item;
605 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
606 modelShortcut.intent.filterEquals(shortcut.intent) &&
607 modelShortcut.id == shortcut.id &&
608 modelShortcut.itemType == shortcut.itemType &&
609 modelShortcut.container == shortcut.container &&
Adam Cohendcd297f2013-06-18 13:13:40 -0700610 modelShortcut.screenId == shortcut.screenId &&
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700611 modelShortcut.cellX == shortcut.cellX &&
612 modelShortcut.cellY == shortcut.cellY &&
613 modelShortcut.spanX == shortcut.spanX &&
Sunny Goyalaa8ef112015-06-12 20:04:41 -0700614 modelShortcut.spanY == shortcut.spanY) {
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700615 // For all intents and purposes, this is the same object
616 return;
617 }
618 }
619
620 // the modelItem needs to match up perfectly with item if our model is
621 // to be consistent with the database-- for now, just require
622 // modelItem == item or the equality check above
623 String msg = "item: " + ((item != null) ? item.toString() : "null") +
624 "modelItem: " +
625 ((modelItem != null) ? modelItem.toString() : "null") +
626 "Error: ItemInfo passed to checkItemInfo doesn't match original";
627 RuntimeException e = new RuntimeException(msg);
628 if (stackTrace != null) {
629 e.setStackTrace(stackTrace);
630 }
Adam Cohenb9ada652013-11-08 08:25:08 -0800631 throw e;
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700632 }
633 }
634
Michael Jurka816474f2012-06-25 14:49:02 -0700635 static void checkItemInfo(final ItemInfo item) {
636 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
637 final long itemId = item.id;
638 Runnable r = new Runnable() {
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700639 public void run() {
640 synchronized (sBgLock) {
641 checkItemInfoLocked(itemId, item, stackTrace);
Michael Jurka816474f2012-06-25 14:49:02 -0700642 }
Michael Jurkab2ae8ac2012-09-21 12:06:06 -0700643 }
644 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700645 runOnWorkerThread(r);
Michael Jurka816474f2012-06-25 14:49:02 -0700646 }
647
Michael Jurkac9d95c52011-08-29 14:03:34 -0700648 static void updateItemInDatabaseHelper(Context context, final ContentValues values,
649 final ItemInfo item, final String callingFunction) {
650 final long itemId = item.id;
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700651 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700652 final ContentResolver cr = context.getContentResolver();
653
Adam Cohen487f7dd2012-06-28 18:12:10 -0700654 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700655 Runnable r = new Runnable() {
656 public void run() {
657 cr.update(uri, values, null, null);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700658 updateItemArrays(item, itemId, stackTrace);
659 }
660 };
661 runOnWorkerThread(r);
662 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700663
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700664 static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
665 final ArrayList<ItemInfo> items, final String callingFunction) {
666 final ContentResolver cr = context.getContentResolver();
Adam Cohen487f7dd2012-06-28 18:12:10 -0700667
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700668 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
669 Runnable r = new Runnable() {
670 public void run() {
671 ArrayList<ContentProviderOperation> ops =
672 new ArrayList<ContentProviderOperation>();
673 int count = items.size();
674 for (int i = 0; i < count; i++) {
675 ItemInfo item = items.get(i);
676 final long itemId = item.id;
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700677 final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700678 ContentValues values = valuesList.get(i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700679
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700680 ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
681 updateItemArrays(item, itemId, stackTrace);
682
683 }
684 try {
685 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
686 } catch (Exception e) {
687 e.printStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700688 }
689 }
690 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700691 runOnWorkerThread(r);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700692 }
Adam Cohenbebf0422012-04-11 18:06:28 -0700693
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700694 static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
695 // Lock on mBgLock *after* the db operation
696 synchronized (sBgLock) {
697 checkItemInfoLocked(itemId, item, stackTrace);
698
699 if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
700 item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
701 // Item is in a folder, make sure this folder exists
702 if (!sBgFolders.containsKey(item.container)) {
703 // An items container is being set to a that of an item which is not in
704 // the list of Folders.
705 String msg = "item: " + item + " container being set to: " +
706 item.container + ", not in the list of folders";
707 Log.e(TAG, msg);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700708 }
709 }
710
711 // Items are added/removed from the corresponding FolderInfo elsewhere, such
712 // as in Workspace.onDrop. Here, we just add/remove them from the list of items
713 // that are on the desktop, as appropriate
714 ItemInfo modelItem = sBgItemsIdMap.get(itemId);
Winson Chung33231f52013-12-09 16:57:45 -0800715 if (modelItem != null &&
716 (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
717 modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700718 switch (modelItem.itemType) {
719 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
720 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
721 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
722 if (!sBgWorkspaceItems.contains(modelItem)) {
723 sBgWorkspaceItems.add(modelItem);
724 }
725 break;
726 default:
727 break;
728 }
729 } else {
730 sBgWorkspaceItems.remove(modelItem);
731 }
732 }
733 }
734
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800735 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400736 * Move an item in the DB to a new <container, screen, cellX, cellY>
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700737 */
Sunny Goyal83a8f042015-05-19 12:52:12 -0700738 public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700739 final long screenId, final int cellX, final int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400740 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400741 item.cellX = cellX;
742 item.cellY = cellY;
Michael Jurkac9d95c52011-08-29 14:03:34 -0700743
Winson Chung3d503fb2011-07-13 17:25:49 -0700744 // We store hotseat items in canonical form which is this orientation invariant position
745 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700746 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -0700747 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700748 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -0700749 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700750 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -0700751 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400752
753 final ContentValues values = new ContentValues();
Joe Onorato9c1289c2009-08-17 11:03:03 -0400754 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Winson Chung3d503fb2011-07-13 17:25:49 -0700755 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
756 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Sunny Goyal08f72612015-01-05 13:41:43 -0800757 values.put(LauncherSettings.Favorites.RANK, item.rank);
Adam Cohendcd297f2013-06-18 13:13:40 -0700758 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400759
Michael Jurkac9d95c52011-08-29 14:03:34 -0700760 updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
The Android Open Source Projectbc219c32009-03-09 11:52:14 -0700761 }
762
763 /**
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700764 * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
765 * cellX, cellY have already been updated on the ItemInfos.
766 */
Adam Cohenf9c184a2016-01-15 16:47:43 -0800767 public static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700768 final long container, final int screen) {
769
770 ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
771 int count = items.size();
772
773 for (int i = 0; i < count; i++) {
774 ItemInfo item = items.get(i);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700775 item.container = container;
776
777 // We store hotseat items in canonical form which is this orientation invariant position
778 // in the hotseat
779 if (context instanceof Launcher && screen < 0 &&
780 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700781 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700782 item.cellY);
783 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700784 item.screenId = screen;
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700785 }
786
787 final ContentValues values = new ContentValues();
788 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
789 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
790 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Sunny Goyal08f72612015-01-05 13:41:43 -0800791 values.put(LauncherSettings.Favorites.RANK, item.rank);
Adam Cohendcd297f2013-06-18 13:13:40 -0700792 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohenf0f4eda2013-06-06 21:27:03 -0700793
794 contentValues.add(values);
795 }
796 updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
797 }
798
799 /**
Adam Cohenbebf0422012-04-11 18:06:28 -0700800 * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
Adam Cohend4844c32011-02-18 19:25:06 -0800801 */
Adam Cohenbebf0422012-04-11 18:06:28 -0700802 static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
Adam Cohendcd297f2013-06-18 13:13:40 -0700803 final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
Winson Chung0f84a602013-09-30 14:30:58 -0700804 item.container = container;
Adam Cohend4844c32011-02-18 19:25:06 -0800805 item.cellX = cellX;
806 item.cellY = cellY;
Adam Cohenbebf0422012-04-11 18:06:28 -0700807 item.spanX = spanX;
808 item.spanY = spanY;
809
810 // We store hotseat items in canonical form which is this orientation invariant position
811 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700812 if (context instanceof Launcher && screenId < 0 &&
Adam Cohenbebf0422012-04-11 18:06:28 -0700813 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700814 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Adam Cohenbebf0422012-04-11 18:06:28 -0700815 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700816 item.screenId = screenId;
Adam Cohenbebf0422012-04-11 18:06:28 -0700817 }
Adam Cohend4844c32011-02-18 19:25:06 -0800818
Adam Cohend4844c32011-02-18 19:25:06 -0800819 final ContentValues values = new ContentValues();
Adam Cohend4844c32011-02-18 19:25:06 -0800820 values.put(LauncherSettings.Favorites.CONTAINER, item.container);
Adam Cohenbebf0422012-04-11 18:06:28 -0700821 values.put(LauncherSettings.Favorites.CELLX, item.cellX);
822 values.put(LauncherSettings.Favorites.CELLY, item.cellY);
Sunny Goyal08f72612015-01-05 13:41:43 -0800823 values.put(LauncherSettings.Favorites.RANK, item.rank);
Adam Cohenbebf0422012-04-11 18:06:28 -0700824 values.put(LauncherSettings.Favorites.SPANX, item.spanX);
825 values.put(LauncherSettings.Favorites.SPANY, item.spanY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700826 values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
Adam Cohend4844c32011-02-18 19:25:06 -0800827
Michael Jurka816474f2012-06-25 14:49:02 -0700828 updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
Adam Cohenbebf0422012-04-11 18:06:28 -0700829 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700830
831 /**
832 * Update an item to the database in a specified container.
833 */
Sunny Goyal83a8f042015-05-19 12:52:12 -0700834 public static void updateItemInDatabase(Context context, final ItemInfo item) {
Michael Jurkac9d95c52011-08-29 14:03:34 -0700835 final ContentValues values = new ContentValues();
Kenny Guyed131872014-04-30 03:02:21 +0100836 item.onAddToDatabase(context, values);
Michael Jurkac9d95c52011-08-29 14:03:34 -0700837 updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
Adam Cohend4844c32011-02-18 19:25:06 -0800838 }
839
Sunny Goyal756a28a2015-04-23 17:07:55 -0700840 private void assertWorkspaceLoaded() {
Sunny Goyal8f531dd2015-08-25 17:08:57 -0700841 if (ProviderConfig.IS_DOGFOOD_BUILD) {
Sunny Goyal639e9062015-08-19 19:17:06 -0700842 synchronized (mLock) {
843 if (!mHasLoaderCompletedOnce ||
844 (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
845 throw new RuntimeException("Trying to add shortcut while loader is running");
846 }
847 }
Sunny Goyal756a28a2015-04-23 17:07:55 -0700848 }
849 }
850
Adam Cohend4844c32011-02-18 19:25:06 -0800851 /**
Sunny Goyal756adbc2015-04-16 15:20:51 -0700852 * Returns true if the shortcuts already exists on the workspace. This must be called after
853 * the workspace has been loaded. We identify a shortcut by its intent.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800854 */
Sunny Goyal756a28a2015-04-23 17:07:55 -0700855 @Thunk boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) {
856 assertWorkspaceLoaded();
Sunny Goyaldfde9992015-05-01 12:31:32 -0700857 final String intentWithPkg, intentWithoutPkg;
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700858 if (intent.getComponent() != null) {
859 // If component is not null, an intent with null package will produce
860 // the same result and should also be a match.
Sunny Goyal4e5cc642015-06-25 16:37:44 -0700861 String packageName = intent.getComponent().getPackageName();
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700862 if (intent.getPackage() != null) {
Sunny Goyaldfde9992015-05-01 12:31:32 -0700863 intentWithPkg = intent.toUri(0);
864 intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700865 } else {
Sunny Goyaldfde9992015-05-01 12:31:32 -0700866 intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
867 intentWithoutPkg = intent.toUri(0);
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700868 }
869 } else {
Sunny Goyaldfde9992015-05-01 12:31:32 -0700870 intentWithPkg = intent.toUri(0);
871 intentWithoutPkg = intent.toUri(0);
Sunny Goyal2a6cf092014-06-26 15:27:14 -0700872 }
Sunny Goyal756adbc2015-04-16 15:20:51 -0700873
874 synchronized (sBgLock) {
Sunny Goyale2df0622015-04-24 11:27:00 -0700875 for (ItemInfo item : sBgItemsIdMap) {
Sunny Goyal756adbc2015-04-16 15:20:51 -0700876 if (item instanceof ShortcutInfo) {
877 ShortcutInfo info = (ShortcutInfo) item;
Sunny Goyal4e5cc642015-06-25 16:37:44 -0700878 Intent targetIntent = info.promisedIntent == null
879 ? info.intent : info.promisedIntent;
880 if (targetIntent != null && info.user.equals(user)) {
881 String s = targetIntent.toUri(0);
Sunny Goyaldfde9992015-05-01 12:31:32 -0700882 if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
883 return true;
884 }
Sunny Goyal756adbc2015-04-16 15:20:51 -0700885 }
886 }
887 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800888 }
Sunny Goyal756adbc2015-04-16 15:20:51 -0700889 return false;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700890 }
891
Joe Onorato9c1289c2009-08-17 11:03:03 -0400892 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -0400893 * Add an item to the database in a specified container. Sets the container, screen, cellX and
894 * cellY fields of the item. Also assigns an ID to the item.
895 */
Sunny Goyal18bf8e22015-04-08 18:13:46 -0700896 public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700897 final long screenId, final int cellX, final int cellY) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400898 item.container = container;
Joe Onorato9c1289c2009-08-17 11:03:03 -0400899 item.cellX = cellX;
900 item.cellY = cellY;
Winson Chung3d503fb2011-07-13 17:25:49 -0700901 // We store hotseat items in canonical form which is this orientation invariant position
902 // in the hotseat
Adam Cohendcd297f2013-06-18 13:13:40 -0700903 if (context instanceof Launcher && screenId < 0 &&
Winson Chung3d503fb2011-07-13 17:25:49 -0700904 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700905 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
Winson Chung3d503fb2011-07-13 17:25:49 -0700906 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700907 item.screenId = screenId;
Winson Chung3d503fb2011-07-13 17:25:49 -0700908 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400909
910 final ContentValues values = new ContentValues();
911 final ContentResolver cr = context.getContentResolver();
Kenny Guyed131872014-04-30 03:02:21 +0100912 item.onAddToDatabase(context, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400913
Sunny Goyald2497482015-09-22 18:24:19 -0700914 item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
915 .getLong(LauncherSettings.Settings.EXTRA_VALUE);
916
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700917 values.put(LauncherSettings.Favorites._ID, item.id);
Winson Chungaafa03c2010-06-11 17:34:16 -0700918
Jason Monk8e19cf22014-03-20 15:06:57 -0400919 final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Michael Jurkac9d95c52011-08-29 14:03:34 -0700920 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700921 public void run() {
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700922 cr.insert(LauncherSettings.Favorites.CONTENT_URI, values);
Joe Onorato9c1289c2009-08-17 11:03:03 -0400923
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700924 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -0700925 synchronized (sBgLock) {
Jason Monk8e19cf22014-03-20 15:06:57 -0400926 checkItemInfoLocked(item.id, item, stackTrace);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700927 sBgItemsIdMap.put(item.id, item);
928 switch (item.itemType) {
929 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
930 sBgFolders.put(item.id, (FolderInfo) item);
931 // Fall through
932 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
933 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
934 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
935 item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
936 sBgWorkspaceItems.add(item);
937 } else {
938 if (!sBgFolders.containsKey(item.container)) {
939 // Adding an item to a folder that doesn't exist.
940 String msg = "adding item: " + item + " to a folder that " +
941 " doesn't exist";
Adam Cohen28b3e102012-10-04 17:21:33 -0700942 Log.e(TAG, msg);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700943 }
Adam Cohen487f7dd2012-06-28 18:12:10 -0700944 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700945 break;
946 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
947 sBgAppWidgets.add((LauncherAppWidgetInfo) item);
948 break;
949 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700950 }
951 }
Michael Jurkac9d95c52011-08-29 14:03:34 -0700952 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700953 runOnWorkerThread(r);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700954 }
955
Sunny Goyal34942622014-08-29 17:20:55 -0700956 private static ArrayList<ItemInfo> getItemsByPackageName(
957 final String pn, final UserHandleCompat user) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700958 ItemInfoFilter filter = new ItemInfoFilter() {
959 @Override
960 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
961 return cn.getPackageName().equals(pn) && info.user.equals(user);
962 }
963 };
Sunny Goyale2df0622015-04-24 11:27:00 -0700964 return filterItemInfos(sBgItemsIdMap, filter);
Sunny Goyal34942622014-08-29 17:20:55 -0700965 }
966
967 /**
968 * Removes all the items from the database corresponding to the specified package.
969 */
970 static void deletePackageFromDatabase(Context context, final String pn,
971 final UserHandleCompat user) {
972 deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700973 }
974
975 /**
Michael Jurkac9d95c52011-08-29 14:03:34 -0700976 * Removes the specified item from the database
Joe Onorato9c1289c2009-08-17 11:03:03 -0400977 */
Sunny Goyalfa401a12015-04-10 13:45:42 -0700978 public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700979 ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
980 items.add(item);
981 deleteItemsFromDatabase(context, items);
982 }
983
984 /**
985 * Removes the specified items from the database
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700986 */
Sunny Goyal4390ace2014-10-13 11:33:11 -0700987 static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400988 final ContentResolver cr = context.getContentResolver();
Michael Jurka83df1882011-08-31 20:59:26 -0700989 Runnable r = new Runnable() {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700990 public void run() {
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700991 for (ItemInfo item : items) {
Sunny Goyal1d4a2df2015-03-30 11:11:46 -0700992 final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700993 cr.delete(uri, null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -0700994
Sunny Goyale7b8cd92014-08-27 14:04:33 -0700995 // Lock on mBgLock *after* the db operation
996 synchronized (sBgLock) {
997 switch (item.itemType) {
998 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
999 sBgFolders.remove(item.id);
Sunny Goyale2df0622015-04-24 11:27:00 -07001000 for (ItemInfo info: sBgItemsIdMap) {
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001001 if (info.container == item.id) {
1002 // We are deleting a folder which still contains items that
1003 // think they are contained by that folder.
1004 String msg = "deleting a folder (" + item + ") which still " +
1005 "contains items (" + info + ")";
1006 Log.e(TAG, msg);
1007 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001008 }
Sunny Goyale7b8cd92014-08-27 14:04:33 -07001009 sBgWorkspaceItems.remove(item);
1010 break;
1011 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1012 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1013 sBgWorkspaceItems.remove(item);
1014 break;
1015 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1016 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
1017 break;
1018 }
1019 sBgItemsIdMap.remove(item.id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001020 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001021 }
1022 }
Michael Jurka83df1882011-08-31 20:59:26 -07001023 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001024 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001025 }
1026
1027 /**
Adam Cohendcd297f2013-06-18 13:13:40 -07001028 * Update the order of the workspace screens in the database. The array list contains
1029 * a list of screen ids in the order that they should appear.
1030 */
Sunny Goyale5bb7052015-07-27 14:36:07 -07001031 public void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
Winson Chung64359a52013-07-08 17:17:08 -07001032 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
Adam Cohendcd297f2013-06-18 13:13:40 -07001033 final ContentResolver cr = context.getContentResolver();
1034 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
1035
1036 // Remove any negative screen ids -- these aren't persisted
Winson Chung64359a52013-07-08 17:17:08 -07001037 Iterator<Long> iter = screensCopy.iterator();
Adam Cohendcd297f2013-06-18 13:13:40 -07001038 while (iter.hasNext()) {
1039 long id = iter.next();
1040 if (id < 0) {
1041 iter.remove();
1042 }
1043 }
1044
1045 Runnable r = new Runnable() {
1046 @Override
1047 public void run() {
Yura085c8532014-02-11 15:15:29 +00001048 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
Adam Cohendcd297f2013-06-18 13:13:40 -07001049 // Clear the table
Yura085c8532014-02-11 15:15:29 +00001050 ops.add(ContentProviderOperation.newDelete(uri).build());
Winson Chung76828c82013-08-19 15:43:29 -07001051 int count = screensCopy.size();
Adam Cohendcd297f2013-06-18 13:13:40 -07001052 for (int i = 0; i < count; i++) {
1053 ContentValues v = new ContentValues();
Winson Chung76828c82013-08-19 15:43:29 -07001054 long screenId = screensCopy.get(i);
Adam Cohendcd297f2013-06-18 13:13:40 -07001055 v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
1056 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
Yura085c8532014-02-11 15:15:29 +00001057 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
Adam Cohendcd297f2013-06-18 13:13:40 -07001058 }
Yura085c8532014-02-11 15:15:29 +00001059
1060 try {
1061 cr.applyBatch(LauncherProvider.AUTHORITY, ops);
1062 } catch (Exception ex) {
1063 throw new RuntimeException(ex);
1064 }
Winson Chung9e6a0a22013-08-27 11:58:12 -07001065
Winson Chungba9c37f2013-08-30 14:11:37 -07001066 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001067 sBgWorkspaceScreens.clear();
1068 sBgWorkspaceScreens.addAll(screensCopy);
Adam Cohen4caf2982013-08-20 18:54:31 -07001069 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001070 }
1071 };
1072 runOnWorkerThread(r);
1073 }
1074
1075 /**
Winsonc0b52fe2015-09-09 16:38:15 -07001076 * Remove the specified folder and all its contents from the database.
Joe Onorato9c1289c2009-08-17 11:03:03 -04001077 */
Winsonc0b52fe2015-09-09 16:38:15 -07001078 public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04001079 final ContentResolver cr = context.getContentResolver();
1080
Michael Jurkac9d95c52011-08-29 14:03:34 -07001081 Runnable r = new Runnable() {
1082 public void run() {
Sunny Goyal1d4a2df2015-03-30 11:11:46 -07001083 cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001084 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001085 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001086 sBgItemsIdMap.remove(info.id);
1087 sBgFolders.remove(info.id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001088 sBgWorkspaceItems.remove(info);
1089 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001090
Sunny Goyal1d4a2df2015-03-30 11:11:46 -07001091 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
Michael Jurkac9d95c52011-08-29 14:03:34 -07001092 LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001093 // Lock on mBgLock *after* the db operation
Winson Chung2abf94d2012-07-18 18:16:38 -07001094 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001095 for (ItemInfo childInfo : info.contents) {
1096 sBgItemsIdMap.remove(childInfo.id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001097 }
Adam Cohenafb01ee2011-06-23 15:38:03 -07001098 }
Michael Jurkac9d95c52011-08-29 14:03:34 -07001099 }
1100 };
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001101 runOnWorkerThread(r);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001102 }
1103
1104 /**
1105 * Set this as the current Launcher activity object for the loader.
1106 */
1107 public void initialize(Callbacks callbacks) {
1108 synchronized (mLock) {
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -07001109 Preconditions.assertUIThread();
1110 // Remove any queued UI runnables
1111 mHandler.cancelAll();
1112 mCallbacks = new WeakReference<>(callbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001113 }
1114 }
1115
Kenny Guyed131872014-04-30 03:02:21 +01001116 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001117 public void onPackageChanged(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001118 int op = PackageUpdatedTask.OP_UPDATE;
1119 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1120 user));
1121 }
1122
1123 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001124 public void onPackageRemoved(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001125 int op = PackageUpdatedTask.OP_REMOVE;
1126 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1127 user));
1128 }
1129
1130 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001131 public void onPackageAdded(String packageName, UserHandleCompat user) {
Kenny Guyed131872014-04-30 03:02:21 +01001132 int op = PackageUpdatedTask.OP_ADD;
1133 enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
1134 user));
1135 }
1136
1137 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001138 public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
Kenny Guyed131872014-04-30 03:02:21 +01001139 boolean replacing) {
Sunny Goyal144154d2016-03-30 16:47:03 -07001140 enqueuePackageUpdated(
1141 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
Kenny Guyed131872014-04-30 03:02:21 +01001142 }
1143
1144 @Override
Kenny Guyc2bd8102014-06-30 12:30:31 +01001145 public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
Kenny Guyed131872014-04-30 03:02:21 +01001146 boolean replacing) {
1147 if (!replacing) {
1148 enqueuePackageUpdated(new PackageUpdatedTask(
1149 PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
1150 user));
1151 }
Kenny Guyed131872014-04-30 03:02:21 +01001152 }
1153
Kenny Guy44cba692016-01-21 19:50:02 +00001154 @Override
1155 public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
1156 enqueuePackageUpdated(new PackageUpdatedTask(
1157 PackageUpdatedTask.OP_SUSPEND, packageNames,
1158 user));
1159 }
1160
1161 @Override
1162 public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
1163 enqueuePackageUpdated(new PackageUpdatedTask(
1164 PackageUpdatedTask.OP_UNSUSPEND, packageNames,
1165 user));
1166 }
1167
Joe Onorato1d8e7bb2009-10-15 19:49:43 -07001168 /**
Joe Onorato9c1289c2009-08-17 11:03:03 -04001169 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
1170 * ACTION_PACKAGE_CHANGED.
1171 */
Narayan Kamathcb1a4772011-06-28 13:46:59 +01001172 @Override
Joe Onoratof99f8c12009-10-31 17:27:36 -04001173 public void onReceive(Context context, Intent intent) {
Chris Wrenb358f812014-04-16 13:37:00 -04001174 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
Winson Chungaafa03c2010-06-11 17:34:16 -07001175
Joe Onorato36115782010-06-17 13:28:48 -04001176 final String action = intent.getAction();
Kenny Guyed131872014-04-30 03:02:21 +01001177 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Reena Lee93f824a2011-09-23 17:20:28 -07001178 // If we have changed locale we need to clear out the labels in all apps/workspace.
1179 forceReload();
Winson Chung88fa7412015-08-03 14:25:28 -07001180 } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
Sunny Goyale0f58d72014-11-10 18:05:31 -08001181 Callbacks callbacks = getCallback();
1182 if (callbacks != null) {
Winson Chung88fa7412015-08-03 14:25:28 -07001183 callbacks.bindSearchProviderChanged();
Winson Chungcfdf7ee2011-08-25 11:38:34 -07001184 }
Sunny Goyal957c13f2015-05-01 13:02:20 -07001185 } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
Sunny Goyalda891c12016-03-18 18:29:24 -07001186 || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
Sunny Goyal823fd502015-08-04 11:40:13 -07001187 UserManagerCompat.getInstance(context).enableAndResetCache();
Sunny Goyal957c13f2015-05-01 13:02:20 -07001188 forceReload();
Rubin Xuac6e5d72016-04-04 16:13:35 +01001189 } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
1190 LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
Sunny Goyalda891c12016-03-18 18:29:24 -07001191 UserHandleCompat user = UserHandleCompat.fromIntent(intent);
1192 if (user != null) {
1193 enqueuePackageUpdated(new PackageUpdatedTask(
1194 PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
1195 new String[0], user));
1196 }
Tony Wickham827cef22016-03-17 15:39:39 -07001197 } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
1198 ExtractionUtils.startColorExtractionServiceIfNecessary(context);
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001199 }
1200 }
1201
Amith Yamasani6cc806d2014-05-02 13:47:11 -07001202 void forceReload() {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001203 resetLoadedState(true, true);
1204
Reena Lee93f824a2011-09-23 17:20:28 -07001205 // Do this here because if the launcher activity is running it will be restarted.
1206 // If it's not running startLoaderFromBackground will merely tell it that it needs
1207 // to reload.
1208 startLoaderFromBackground();
1209 }
1210
Winson Chungf0c6ae02012-03-21 16:10:31 -07001211 public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
1212 synchronized (mLock) {
1213 // Stop any existing loaders first, so they don't set mAllAppsLoaded or
1214 // mWorkspaceLoaded to true later
1215 stopLoaderLocked();
1216 if (resetAllAppsLoaded) mAllAppsLoaded = false;
1217 if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
1218 }
1219 }
1220
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001221 /**
1222 * When the launcher is in the background, it's possible for it to miss paired
1223 * configuration changes. So whenever we trigger the loader from the background
1224 * tell the launcher that it needs to re-run the loader when it comes back instead
1225 * of doing it now.
1226 */
1227 public void startLoaderFromBackground() {
Sunny Goyale0f58d72014-11-10 18:05:31 -08001228 Callbacks callbacks = getCallback();
1229 if (callbacks != null) {
1230 // Only actually run the loader if they're not paused.
1231 if (!callbacks.setLoadOnResume()) {
Sunny Goyal93f878c2016-03-30 17:31:24 -07001232 startLoader(callbacks.getCurrentWorkspaceScreen());
Joe Onoratoe9ad59e2010-10-29 17:35:36 -07001233 }
1234 }
Joe Onorato36115782010-06-17 13:28:48 -04001235 }
Joe Onoratof99f8c12009-10-31 17:27:36 -04001236
Sunny Goyal2bba4c32015-05-18 15:42:48 -07001237 /**
1238 * If there is already a loader task running, tell it to stop.
1239 */
1240 private void stopLoaderLocked() {
Reena Lee93f824a2011-09-23 17:20:28 -07001241 LoaderTask oldTask = mLoaderTask;
1242 if (oldTask != null) {
Reena Lee93f824a2011-09-23 17:20:28 -07001243 oldTask.stopLocked();
1244 }
Reena Lee93f824a2011-09-23 17:20:28 -07001245 }
1246
Adam Cohen1a85c582014-09-30 09:48:49 -07001247 public boolean isCurrentCallbacks(Callbacks callbacks) {
1248 return (mCallbacks != null && mCallbacks.get() == callbacks);
1249 }
1250
Sunny Goyal2bba4c32015-05-18 15:42:48 -07001251 public void startLoader(int synchronousBindPage) {
1252 startLoader(synchronousBindPage, LOADER_FLAG_NONE);
Dan Sandlerd5024042014-01-09 15:01:33 -05001253 }
1254
Sunny Goyal2bba4c32015-05-18 15:42:48 -07001255 public void startLoader(int synchronousBindPage, int loadFlags) {
Sunny Goyal756adbc2015-04-16 15:20:51 -07001256 // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
1257 InstallShortcutReceiver.enableInstallQueue();
Joe Onorato36115782010-06-17 13:28:48 -04001258 synchronized (mLock) {
Joe Onorato36115782010-06-17 13:28:48 -04001259 // Don't bother to start the thread if we know it's not going to do anything
1260 if (mCallbacks != null && mCallbacks.get() != null) {
Sunny Goyal527c7d32015-08-28 15:19:36 -07001261 final Callbacks oldCallbacks = mCallbacks.get();
1262 // Clear any pending bind-runnables from the synchronized load process.
1263 runOnMainThread(new Runnable() {
1264 public void run() {
1265 oldCallbacks.clearPendingBinds();
1266 }
1267 });
1268
Joe Onorato36115782010-06-17 13:28:48 -04001269 // If there is already one running, tell it to stop.
Sunny Goyal2bba4c32015-05-18 15:42:48 -07001270 stopLoaderLocked();
Sunny Goyal93f878c2016-03-30 17:31:24 -07001271 mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags, synchronousBindPage);
Derek Prothro7aff3992013-12-10 14:00:37 -05001272 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
Sunny Goyalf5cd9982015-05-18 15:19:29 -07001273 && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001274 mLoaderTask.runBindSynchronousPage(synchronousBindPage);
1275 } else {
1276 sWorkerThread.setPriority(Thread.NORM_PRIORITY);
1277 sWorker.post(mLoaderTask);
1278 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001279 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001280 }
1281 }
1282
Joe Onorato36115782010-06-17 13:28:48 -04001283 public void stopLoader() {
1284 synchronized (mLock) {
1285 if (mLoaderTask != null) {
1286 mLoaderTask.stopLocked();
Joe Onorato9c1289c2009-08-17 11:03:03 -04001287 }
1288 }
Joe Onorato36115782010-06-17 13:28:48 -04001289 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001290
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -08001291 /**
1292 * Loads the workspace screen ids in an ordered list.
1293 */
Sunny Goyale5bb7052015-07-27 14:36:07 -07001294 public static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
Winson Chung76828c82013-08-19 15:43:29 -07001295 final ContentResolver contentResolver = context.getContentResolver();
1296 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
Winson Chung76828c82013-08-19 15:43:29 -07001297
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -08001298 // Get screens ordered by rank.
1299 final Cursor sc = contentResolver.query(screensUri, null, null, null,
1300 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
1301 ArrayList<Long> screenIds = new ArrayList<Long>();
Winson Chung76828c82013-08-19 15:43:29 -07001302 try {
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -08001303 final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID);
Winson Chung76828c82013-08-19 15:43:29 -07001304 while (sc.moveToNext()) {
1305 try {
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -08001306 screenIds.add(sc.getLong(idIndex));
Winson Chung76828c82013-08-19 15:43:29 -07001307 } catch (Exception e) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07001308 FileLog.d(TAG, "Invalid screen id", e);
Winson Chung76828c82013-08-19 15:43:29 -07001309 }
1310 }
1311 } finally {
1312 sc.close();
1313 }
Sunny Goyalc1b7c2e2015-01-16 16:19:04 -08001314 return screenIds;
Winson Chung76828c82013-08-19 15:43:29 -07001315 }
1316
Joe Onorato36115782010-06-17 13:28:48 -04001317 /**
1318 * Runnable for the thread that loads the contents of the launcher:
1319 * - workspace icons
1320 * - widgets
1321 * - all apps icons
1322 */
1323 private class LoaderTask implements Runnable {
1324 private Context mContext;
Sunny Goyal93f878c2016-03-30 17:31:24 -07001325 private int mPageToBindFirst;
1326
Adam Cohen091440a2015-03-18 14:16:05 -07001327 @Thunk boolean mIsLoadingAndBindingWorkspace;
Joe Onorato36115782010-06-17 13:28:48 -04001328 private boolean mStopped;
Adam Cohen091440a2015-03-18 14:16:05 -07001329 @Thunk boolean mLoadAndBindStepFinished;
Dan Sandlerd5024042014-01-09 15:01:33 -05001330 private int mFlags;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001331
Sunny Goyal93f878c2016-03-30 17:31:24 -07001332 LoaderTask(Context context, int flags, int pageToBindFirst) {
Joe Onorato36115782010-06-17 13:28:48 -04001333 mContext = context;
Dan Sandlerd5024042014-01-09 15:01:33 -05001334 mFlags = flags;
Sunny Goyal93f878c2016-03-30 17:31:24 -07001335 mPageToBindFirst = pageToBindFirst;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001336 }
1337
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001338 private void loadAndBindWorkspace() {
Winson Chung36a62fe2012-05-06 18:04:42 -07001339 mIsLoadingAndBindingWorkspace = true;
1340
Joe Onorato36115782010-06-17 13:28:48 -04001341 // Load the workspace
Joe Onorato36115782010-06-17 13:28:48 -04001342 if (DEBUG_LOADERS) {
1343 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001344 }
Michael Jurka288a36b2011-07-12 16:53:48 -07001345
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001346 if (!mWorkspaceLoaded) {
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001347 loadWorkspace();
Reena Lee93f824a2011-09-23 17:20:28 -07001348 synchronized (LoaderTask.this) {
1349 if (mStopped) {
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001350 return;
Reena Lee93f824a2011-09-23 17:20:28 -07001351 }
1352 mWorkspaceLoaded = true;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001353 }
1354 }
1355
Joe Onorato36115782010-06-17 13:28:48 -04001356 // Bind the workspace
Sunny Goyal93f878c2016-03-30 17:31:24 -07001357 bindWorkspace(mPageToBindFirst);
Joe Onorato36115782010-06-17 13:28:48 -04001358 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001359
Joe Onorato36115782010-06-17 13:28:48 -04001360 private void waitForIdle() {
1361 // Wait until the either we're stopped or the other threads are done.
1362 // This way we don't start loading all apps until the workspace has settled
1363 // down.
1364 synchronized (LoaderTask.this) {
1365 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onoratocc67f472010-06-08 10:54:30 -07001366
Joe Onorato36115782010-06-17 13:28:48 -04001367 mHandler.postIdle(new Runnable() {
1368 public void run() {
1369 synchronized (LoaderTask.this) {
1370 mLoadAndBindStepFinished = true;
1371 if (DEBUG_LOADERS) {
1372 Log.d(TAG, "done with previous binding step");
Daniel Sandler843e8602010-06-07 14:59:01 -04001373 }
Joe Onorato36115782010-06-17 13:28:48 -04001374 LoaderTask.this.notify();
Daniel Sandler843e8602010-06-07 14:59:01 -04001375 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001376 }
Joe Onorato36115782010-06-17 13:28:48 -04001377 });
1378
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08001379 while (!mStopped && !mLoadAndBindStepFinished) {
Joe Onorato36115782010-06-17 13:28:48 -04001380 try {
Michael Jurkac7700af2013-05-14 20:17:58 +02001381 // Just in case mFlushingWorkerThread changes but we aren't woken up,
1382 // wait no longer than 1sec at a time
1383 this.wait(1000);
Joe Onorato36115782010-06-17 13:28:48 -04001384 } catch (InterruptedException ex) {
1385 // Ignore
Daniel Sandler843e8602010-06-07 14:59:01 -04001386 }
1387 }
Joe Onorato36115782010-06-17 13:28:48 -04001388 if (DEBUG_LOADERS) {
1389 Log.d(TAG, "waited "
Winson Chungaafa03c2010-06-11 17:34:16 -07001390 + (SystemClock.uptimeMillis()-workspaceWaitTime)
Joe Onorato36115782010-06-17 13:28:48 -04001391 + "ms for previous step to finish binding");
1392 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001393 }
Joe Onorato36115782010-06-17 13:28:48 -04001394 }
Daniel Sandler843e8602010-06-07 14:59:01 -04001395
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001396 void runBindSynchronousPage(int synchronousBindPage) {
Derek Prothro7aff3992013-12-10 14:00:37 -05001397 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001398 // Ensure that we have a valid page index to load synchronously
1399 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
1400 "valid page index");
1401 }
1402 if (!mAllAppsLoaded || !mWorkspaceLoaded) {
1403 // Ensure that we don't try and bind a specified page when the pages have not been
1404 // loaded already (we should load everything asynchronously in that case)
1405 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
1406 }
1407 synchronized (mLock) {
1408 if (mIsLoaderTaskRunning) {
1409 // Ensure that we are never running the background loading at this point since
1410 // we also touch the background collections
1411 throw new RuntimeException("Error! Background loading is already running");
1412 }
1413 }
1414
1415 // XXX: Throw an exception if we are already loading (since we touch the worker thread
1416 // data structures, we can't allow any other thread to touch that data, but because
1417 // this call is synchronous, we can get away with not locking).
1418
Daniel Sandlercc8befa2013-06-11 14:45:48 -04001419 // The LauncherModel is static in the LauncherAppState and mHandler may have queued
Adam Cohena13a2f22012-07-23 14:29:15 -07001420 // operations from the previous activity. We need to ensure that all queued operations
1421 // are executed before any synchronous binding work is done.
1422 mHandler.flush();
1423
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001424 // Divide the set of loaded items into those that we are binding synchronously, and
1425 // everything else that is to be bound normally (asynchronously).
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001426 bindWorkspace(synchronousBindPage);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001427 // XXX: For now, continue posting the binding of AllApps as there are other issues that
1428 // arise from that.
1429 onlyBindAllApps();
1430 }
1431
Joe Onorato36115782010-06-17 13:28:48 -04001432 public void run() {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001433 synchronized (mLock) {
Sunny Goyalf5cd9982015-05-18 15:19:29 -07001434 if (mStopped) {
1435 return;
1436 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001437 mIsLoaderTaskRunning = true;
1438 }
Joe Onorato36115782010-06-17 13:28:48 -04001439 // Optimize for end-user experience: if the Launcher is up and // running with the
1440 // All Apps interface in the foreground, load All Apps first. Otherwise, load the
1441 // workspace first (default).
Joe Onorato36115782010-06-17 13:28:48 -04001442 keep_running: {
Winson Chung64359a52013-07-08 17:17:08 -07001443 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001444 loadAndBindWorkspace();
Daniel Sandler843e8602010-06-07 14:59:01 -04001445
Joe Onorato36115782010-06-17 13:28:48 -04001446 if (mStopped) {
1447 break keep_running;
1448 }
1449
Joe Onorato36115782010-06-17 13:28:48 -04001450 waitForIdle();
Daniel Sandler843e8602010-06-07 14:59:01 -04001451
1452 // second step
Winson Chung64359a52013-07-08 17:17:08 -07001453 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
1454 loadAndBindAllApps();
Joe Onorato36115782010-06-17 13:28:48 -04001455 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001456
Joe Onorato36115782010-06-17 13:28:48 -04001457 // Clear out this reference, otherwise we end up holding it until all of the
1458 // callback runnables are done.
1459 mContext = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001460
Joe Onorato36115782010-06-17 13:28:48 -04001461 synchronized (mLock) {
1462 // If we are still the last one to be scheduled, remove ourselves.
1463 if (mLoaderTask == this) {
1464 mLoaderTask = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001465 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001466 mIsLoaderTaskRunning = false;
Sunny Goyal756a28a2015-04-23 17:07:55 -07001467 mHasLoaderCompletedOnce = true;
Joe Onorato36115782010-06-17 13:28:48 -04001468 }
Joe Onorato36115782010-06-17 13:28:48 -04001469 }
1470
1471 public void stopLocked() {
1472 synchronized (LoaderTask.this) {
1473 mStopped = true;
1474 this.notify();
1475 }
1476 }
1477
1478 /**
1479 * Gets the callbacks object. If we've been stopped, or if the launcher object
1480 * has somehow been garbage collected, return null instead. Pass in the Callbacks
1481 * object that was around when the deferred message was scheduled, and if there's
1482 * a new Callbacks object around then also return null. This will save us from
1483 * calling onto it with data that will be ignored.
1484 */
1485 Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
1486 synchronized (mLock) {
1487 if (mStopped) {
1488 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001489 }
Joe Onorato36115782010-06-17 13:28:48 -04001490
1491 if (mCallbacks == null) {
1492 return null;
Daniel Sandler8802e962010-05-26 16:28:16 -04001493 }
Joe Onorato36115782010-06-17 13:28:48 -04001494
1495 final Callbacks callbacks = mCallbacks.get();
1496 if (callbacks != oldCallbacks) {
1497 return null;
1498 }
1499 if (callbacks == null) {
1500 Log.w(TAG, "no mCallbacks");
1501 return null;
1502 }
1503
1504 return callbacks;
1505 }
1506 }
1507
1508 // check & update map of what's occupied; used to discard overlapping/invalid items
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001509 private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
1510 ArrayList<Long> workspaceScreens) {
Winson Chung892c74d2013-08-22 16:15:50 -07001511 LauncherAppState app = LauncherAppState.getInstance();
Adam Cohen2e6da152015-05-06 11:42:25 -07001512 InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001513 final int countX = profile.numColumns;
1514 final int countY = profile.numRows;
Winson Chung892c74d2013-08-22 16:15:50 -07001515
Adam Cohendcd297f2013-06-18 13:13:40 -07001516 long containerIndex = item.screenId;
Winson Chungf30ad5f2011-08-08 10:55:42 -07001517 if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Winson Chunga0b7e862013-09-05 16:03:15 -07001518 // Return early if we detect that an item is under the hotseat button
1519 if (mCallbacks == null ||
1520 mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
Dan Sandler295ae182013-12-10 16:05:47 -05001521 Log.e(TAG, "Error loading shortcut into hotseat " + item
1522 + " into position (" + item.screenId + ":" + item.cellX + ","
1523 + item.cellY + ") occupied by all apps");
Winson Chunga0b7e862013-09-05 16:03:15 -07001524 return false;
1525 }
1526
Dan Sandler295ae182013-12-10 16:05:47 -05001527 final ItemInfo[][] hotseatItems =
1528 occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
1529
Adam Cohen2e6da152015-05-06 11:42:25 -07001530 if (item.screenId >= profile.numHotseatIcons) {
Adam Cohenae4409d2013-11-26 10:34:59 -08001531 Log.e(TAG, "Error loading shortcut " + item
1532 + " into hotseat position " + item.screenId
Adam Cohen2e6da152015-05-06 11:42:25 -07001533 + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
Adam Cohenae4409d2013-11-26 10:34:59 -08001534 + ")");
1535 return false;
1536 }
1537
Dan Sandler295ae182013-12-10 16:05:47 -05001538 if (hotseatItems != null) {
1539 if (hotseatItems[(int) item.screenId][0] != null) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001540 Log.e(TAG, "Error loading shortcut into hotseat " + item
1541 + " into position (" + item.screenId + ":" + item.cellX + ","
1542 + item.cellY + ") occupied by "
1543 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
1544 [(int) item.screenId][0]);
1545 return false;
Dan Sandler295ae182013-12-10 16:05:47 -05001546 } else {
1547 hotseatItems[(int) item.screenId][0] = item;
1548 return true;
Adam Cohendcd297f2013-06-18 13:13:40 -07001549 }
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001550 } else {
Adam Cohen2e6da152015-05-06 11:42:25 -07001551 final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001552 items[(int) item.screenId][0] = item;
1553 occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001554 return true;
1555 }
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001556 } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1557 if (!workspaceScreens.contains((Long) item.screenId)) {
1558 // The item has an invalid screen id.
1559 return false;
1560 }
1561 } else {
Winson Chungf30ad5f2011-08-08 10:55:42 -07001562 // Skip further checking if it is not the hotseat or workspace container
Daniel Sandler8802e962010-05-26 16:28:16 -04001563 return true;
1564 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001565
Adam Cohendcd297f2013-06-18 13:13:40 -07001566 if (!occupied.containsKey(item.screenId)) {
Winson Chung892c74d2013-08-22 16:15:50 -07001567 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
Adam Cohendcd297f2013-06-18 13:13:40 -07001568 occupied.put(item.screenId, items);
1569 }
1570
Dan Sandler295ae182013-12-10 16:05:47 -05001571 final ItemInfo[][] screens = occupied.get(item.screenId);
Adam Cohenae4409d2013-11-26 10:34:59 -08001572 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
1573 item.cellX < 0 || item.cellY < 0 ||
1574 item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
1575 Log.e(TAG, "Error loading shortcut " + item
1576 + " into cell (" + containerIndex + "-" + item.screenId + ":"
1577 + item.cellX + "," + item.cellY
1578 + ") out of screen bounds ( " + countX + "x" + countY + ")");
1579 return false;
1580 }
1581
Winson Chung6ba2a1b2011-09-02 16:22:11 -07001582 // Check if any workspace icons overlap with each other
Joe Onorato36115782010-06-17 13:28:48 -04001583 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1584 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001585 if (screens[x][y] != null) {
Joe Onorato36115782010-06-17 13:28:48 -04001586 Log.e(TAG, "Error loading shortcut " + item
Adam Cohendcd297f2013-06-18 13:13:40 -07001587 + " into cell (" + containerIndex + "-" + item.screenId + ":"
Joe Onorato36115782010-06-17 13:28:48 -04001588 + x + "," + y
Winson Chungaafa03c2010-06-11 17:34:16 -07001589 + ") occupied by "
Adam Cohendcd297f2013-06-18 13:13:40 -07001590 + screens[x][y]);
Joe Onorato36115782010-06-17 13:28:48 -04001591 return false;
1592 }
1593 }
1594 }
1595 for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
1596 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001597 screens[x][y] = item;
Joe Onorato36115782010-06-17 13:28:48 -04001598 }
1599 }
Winson Chungf30ad5f2011-08-08 10:55:42 -07001600
Joe Onorato36115782010-06-17 13:28:48 -04001601 return true;
1602 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001603
Winson Chungba9c37f2013-08-30 14:11:37 -07001604 /** Clears all the sBg data structures */
1605 private void clearSBgDataStructures() {
1606 synchronized (sBgLock) {
1607 sBgWorkspaceItems.clear();
1608 sBgAppWidgets.clear();
1609 sBgFolders.clear();
1610 sBgItemsIdMap.clear();
Winson Chungba9c37f2013-08-30 14:11:37 -07001611 sBgWorkspaceScreens.clear();
1612 }
1613 }
1614
Sunny Goyal66cfdc22015-02-02 13:01:51 -08001615 private void loadWorkspace() {
Joe Onorato36115782010-06-17 13:28:48 -04001616 final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001617
Joe Onorato36115782010-06-17 13:28:48 -04001618 final Context context = mContext;
1619 final ContentResolver contentResolver = context.getContentResolver();
1620 final PackageManager manager = context.getPackageManager();
Joe Onorato36115782010-06-17 13:28:48 -04001621 final boolean isSafeMode = manager.isSafeMode();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001622 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
Sunny Goyal25aba0a2015-07-16 15:07:47 -07001623 final boolean isSdCardReady = Utilities.isBootCompleted();
Joe Onorato3c2f7e12009-10-31 19:17:31 -04001624
Winson Chung892c74d2013-08-22 16:15:50 -07001625 LauncherAppState app = LauncherAppState.getInstance();
Adam Cohen2e6da152015-05-06 11:42:25 -07001626 InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001627 int countX = profile.numColumns;
1628 int countY = profile.numRows;
Winson Chung892c74d2013-08-22 16:15:50 -07001629
Sunny Goyalf076eae2016-01-11 12:25:10 -08001630 if (GridSizeMigrationTask.ENABLED &&
1631 !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
1632 // Migration failed. Clear workspace.
1633 mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
Sunny Goyale5bb7052015-07-27 14:36:07 -07001634 }
1635
Dan Sandlerd5024042014-01-09 15:01:33 -05001636 if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
Sunny Goyala1365452015-10-01 15:46:24 -07001637 Log.d(TAG, "loadWorkspace: resetting launcher database");
Sunny Goyald2497482015-09-22 18:24:19 -07001638 LauncherSettings.Settings.call(contentResolver,
1639 LauncherSettings.Settings.METHOD_DELETE_DB);
Dan Sandlerd5024042014-01-09 15:01:33 -05001640 }
1641
1642 if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
1643 // append the user's Launcher2 shortcuts
Sunny Goyala1365452015-10-01 15:46:24 -07001644 Log.d(TAG, "loadWorkspace: migrating from launcher2");
Sunny Goyald2497482015-09-22 18:24:19 -07001645 LauncherSettings.Settings.call(contentResolver,
1646 LauncherSettings.Settings.METHOD_MIGRATE_LAUNCHER2_SHORTCUTS);
Dan Sandlerd5024042014-01-09 15:01:33 -05001647 } else {
1648 // Make sure the default workspace is loaded
Sunny Goyala1365452015-10-01 15:46:24 -07001649 Log.d(TAG, "loadWorkspace: loading default favorites");
Sunny Goyald2497482015-09-22 18:24:19 -07001650 LauncherSettings.Settings.call(contentResolver,
1651 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
Dan Sandlerd5024042014-01-09 15:01:33 -05001652 }
Adam Cohene25af792013-06-06 23:08:25 -07001653
Winson Chung2abf94d2012-07-18 18:16:38 -07001654 synchronized (sBgLock) {
Winson Chungba9c37f2013-08-30 14:11:37 -07001655 clearSBgDataStructures();
Sunny Goyal756adbc2015-04-16 15:20:51 -07001656 final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
Sunny Goyal94485362014-09-18 16:13:58 -07001657 .getInstance(mContext).updateAndGetActiveSessionCache();
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001658 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
Romain Guy5c16f3e2010-01-12 17:24:58 -08001659
Sunny Goyal2e1efb42016-03-03 16:58:55 -08001660 final ArrayList<Long> itemsToRemove = new ArrayList<>();
1661 final ArrayList<Long> restoredRows = new ArrayList<>();
Sunny Goyal1d4a2df2015-03-30 11:11:46 -07001662 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
Chris Wrene523e702013-10-09 10:36:55 -04001663 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
Adam Cohene25af792013-06-06 23:08:25 -07001664 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
Daniel Sandler8802e962010-05-26 16:28:16 -04001665
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001666 // +1 for the hotseat (it can be larger than the workspace)
1667 // Load workspace in reverse order to ensure that latest items are loaded first (and
1668 // before any earlier duplicates)
Sunny Goyale2df0622015-04-24 11:27:00 -07001669 final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
Sunny Goyal2e1efb42016-03-03 16:58:55 -08001670 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001671
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001672 try {
1673 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1674 final int intentIndex = c.getColumnIndexOrThrow
1675 (LauncherSettings.Favorites.INTENT);
1676 final int titleIndex = c.getColumnIndexOrThrow
1677 (LauncherSettings.Favorites.TITLE);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001678 final int containerIndex = c.getColumnIndexOrThrow(
1679 LauncherSettings.Favorites.CONTAINER);
1680 final int itemTypeIndex = c.getColumnIndexOrThrow(
1681 LauncherSettings.Favorites.ITEM_TYPE);
1682 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
1683 LauncherSettings.Favorites.APPWIDGET_ID);
Chris Wrenc3919c02013-09-18 09:48:33 -04001684 final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
1685 LauncherSettings.Favorites.APPWIDGET_PROVIDER);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001686 final int screenIndex = c.getColumnIndexOrThrow(
1687 LauncherSettings.Favorites.SCREEN);
1688 final int cellXIndex = c.getColumnIndexOrThrow
1689 (LauncherSettings.Favorites.CELLX);
1690 final int cellYIndex = c.getColumnIndexOrThrow
1691 (LauncherSettings.Favorites.CELLY);
1692 final int spanXIndex = c.getColumnIndexOrThrow
1693 (LauncherSettings.Favorites.SPANX);
1694 final int spanYIndex = c.getColumnIndexOrThrow(
1695 LauncherSettings.Favorites.SPANY);
Sunny Goyal08f72612015-01-05 13:41:43 -08001696 final int rankIndex = c.getColumnIndexOrThrow(
1697 LauncherSettings.Favorites.RANK);
Chris Wrenf4d08112014-01-16 18:13:56 -05001698 final int restoredIndex = c.getColumnIndexOrThrow(
1699 LauncherSettings.Favorites.RESTORED);
Kenny Guyed131872014-04-30 03:02:21 +01001700 final int profileIdIndex = c.getColumnIndexOrThrow(
1701 LauncherSettings.Favorites.PROFILE_ID);
Sunny Goyal5d85c442015-03-10 13:14:47 -07001702 final int optionsIndex = c.getColumnIndexOrThrow(
1703 LauncherSettings.Favorites.OPTIONS);
Sunny Goyal4e5cc642015-06-25 16:37:44 -07001704 final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001705
Sunny Goyal7f834d22015-04-21 10:10:23 -07001706 final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
Kenny Guyff05f432016-01-22 17:48:29 +00001707 final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
Sunny Goyal7f834d22015-04-21 10:10:23 -07001708 for (UserHandleCompat user : mUserManager.getUserProfiles()) {
Kenny Guyff05f432016-01-22 17:48:29 +00001709 long serialNo = mUserManager.getSerialNumberForUser(user);
1710 allUsers.put(serialNo, user);
1711 quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
Sunny Goyal7f834d22015-04-21 10:10:23 -07001712 }
1713
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001714 ShortcutInfo info;
1715 String intentDescription;
1716 LauncherAppWidgetInfo appWidgetInfo;
1717 int container;
1718 long id;
Robin Lee26ace122015-03-16 19:41:43 +00001719 long serialNumber;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001720 Intent intent;
Kenny Guyed131872014-04-30 03:02:21 +01001721 UserHandleCompat user;
Sunny Goyald09c3702016-04-06 16:18:20 -07001722 String targetPackage;
Joe Onorato9c1289c2009-08-17 11:03:03 -04001723
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001724 while (!mStopped && c.moveToNext()) {
1725 try {
1726 int itemType = c.getInt(itemTypeIndex);
Chris Wrenf4d08112014-01-16 18:13:56 -05001727 boolean restored = 0 != c.getInt(restoredIndex);
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001728 boolean allowMissingTarget = false;
Sunny Goyalb1622cc2015-06-10 16:00:42 -07001729 container = c.getInt(containerIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001730
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001731 switch (itemType) {
1732 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1733 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Winson Chungee055712013-07-30 14:46:24 -07001734 id = c.getLong(idIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001735 intentDescription = c.getString(intentIndex);
Robin Lee26ace122015-03-16 19:41:43 +00001736 serialNumber = c.getInt(profileIdIndex);
Sunny Goyal7f834d22015-04-21 10:10:23 -07001737 user = allUsers.get(serialNumber);
Sunny Goyal34942622014-08-29 17:20:55 -07001738 int promiseType = c.getInt(restoredIndex);
Sunny Goyal1a745e82014-10-02 15:58:31 -07001739 int disabledState = 0;
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001740 boolean itemReplaced = false;
Sunny Goyald09c3702016-04-06 16:18:20 -07001741 targetPackage = null;
Kenny Guyed131872014-04-30 03:02:21 +01001742 if (user == null) {
1743 // User has been deleted remove the item.
1744 itemsToRemove.add(id);
1745 continue;
1746 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001747 try {
1748 intent = Intent.parseUri(intentDescription, 0);
Winson Chungee055712013-07-30 14:46:24 -07001749 ComponentName cn = intent.getComponent();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001750 if (cn != null && cn.getPackageName() != null) {
1751 boolean validPkg = launcherApps.isPackageEnabledForProfile(
1752 cn.getPackageName(), user);
1753 boolean validComponent = validPkg &&
1754 launcherApps.isActivityEnabledForProfile(cn, user);
Sunny Goyald09c3702016-04-06 16:18:20 -07001755 if (validPkg) {
1756 targetPackage = cn.getPackageName();
1757 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001758
1759 if (validComponent) {
1760 if (restored) {
1761 // no special handling necessary for this item
1762 restoredRows.add(id);
1763 restored = false;
1764 }
Kenny Guyff05f432016-01-22 17:48:29 +00001765 if (quietMode.get(serialNumber)) {
Sunny Goyald09c3702016-04-06 16:18:20 -07001766 disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
Kenny Guyff05f432016-01-22 17:48:29 +00001767 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001768 } else if (validPkg) {
Sunny Goyal34942622014-08-29 17:20:55 -07001769 intent = null;
1770 if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
1771 // We allow auto install apps to have their intent
1772 // updated after an install.
1773 intent = manager.getLaunchIntentForPackage(
1774 cn.getPackageName());
1775 if (intent != null) {
1776 ContentValues values = new ContentValues();
1777 values.put(LauncherSettings.Favorites.INTENT,
1778 intent.toUri(0));
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001779 updateItem(id, values);
Sunny Goyal34942622014-08-29 17:20:55 -07001780 }
1781 }
1782
1783 if (intent == null) {
1784 // The app is installed but the component is no
1785 // longer available.
Sunny Goyal713edfc2016-05-06 09:58:34 -07001786 FileLog.d(TAG, "Invalid component removed: " + cn);
Sunny Goyal34942622014-08-29 17:20:55 -07001787 itemsToRemove.add(id);
1788 continue;
1789 } else {
1790 // no special handling necessary for this item
1791 restoredRows.add(id);
1792 restored = false;
1793 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001794 } else if (restored) {
1795 // Package is not yet available but might be
1796 // installed later.
Sunny Goyal713edfc2016-05-06 09:58:34 -07001797 FileLog.d(TAG, "package not yet restored: " + cn);
Sunny Goyal94485362014-09-18 16:13:58 -07001798
1799 if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
1800 // Restore has started once.
Sunny Goyal756adbc2015-04-16 15:20:51 -07001801 } else if (installingPkgs.containsKey(cn.getPackageName())) {
Sunny Goyal94485362014-09-18 16:13:58 -07001802 // App restore has started. Update the flag
1803 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
1804 ContentValues values = new ContentValues();
1805 values.put(LauncherSettings.Favorites.RESTORED,
1806 promiseType);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001807 updateItem(id, values);
1808 } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) {
1809 // This is a common app. Try to replace this.
1810 int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType);
1811 CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context);
1812 if (parser.findDefaultApp()) {
1813 // Default app found. Replace it.
1814 intent = parser.parsedIntent;
1815 cn = intent.getComponent();
1816 ContentValues values = parser.parsedValues;
1817 values.put(LauncherSettings.Favorites.RESTORED, 0);
1818 updateItem(id, values);
1819 restored = false;
1820 itemReplaced = true;
Sunny Goyal94485362014-09-18 16:13:58 -07001821
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001822 } else if (REMOVE_UNRESTORED_ICONS) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07001823 FileLog.d(TAG, "Unrestored package removed: " + cn);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001824 itemsToRemove.add(id);
1825 continue;
1826 }
Sunny Goyal94485362014-09-18 16:13:58 -07001827 } else if (REMOVE_UNRESTORED_ICONS) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07001828 FileLog.d(TAG, "Unrestored package removed: " + cn);
Sunny Goyal94485362014-09-18 16:13:58 -07001829 itemsToRemove.add(id);
1830 continue;
1831 }
Sunny Goyald09c3702016-04-06 16:18:20 -07001832 } else if (PackageManagerHelper.isAppOnSdcard(
1833 manager, cn.getPackageName())) {
Sunny Goyal1a745e82014-10-02 15:58:31 -07001834 // Package is present but not available.
1835 allowMissingTarget = true;
1836 disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
1837 } else if (!isSdCardReady) {
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001838 // SdCard is not ready yet. Package might get available,
1839 // once it is ready.
Sunny Goyala1365452015-10-01 15:46:24 -07001840 Log.d(TAG, "Invalid package: " + cn + " (check again later)");
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001841 HashSet<String> pkgs = sPendingPackages.get(user);
1842 if (pkgs == null) {
Sameer Padala513edae2014-07-29 16:17:08 -07001843 pkgs = new HashSet<String>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07001844 sPendingPackages.put(user, pkgs);
1845 }
1846 pkgs.add(cn.getPackageName());
1847 allowMissingTarget = true;
1848 // Add the icon on the workspace anyway.
Sunny Goyal1a745e82014-10-02 15:58:31 -07001849
1850 } else {
1851 // Do not wait for external media load anymore.
1852 // Log the invalid package, and remove it
Sunny Goyal713edfc2016-05-06 09:58:34 -07001853 FileLog.d(TAG, "Invalid package removed: " + cn);
Sunny Goyal1a745e82014-10-02 15:58:31 -07001854 itemsToRemove.add(id);
1855 continue;
Winson Chungee055712013-07-30 14:46:24 -07001856 }
Sunny Goyal938a53d2014-09-05 03:17:45 -07001857 } else if (cn == null) {
1858 // For shortcuts with no component, keep them as they are
1859 restoredRows.add(id);
1860 restored = false;
Winson Chungee055712013-07-30 14:46:24 -07001861 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001862 } catch (URISyntaxException e) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07001863 FileLog.d(TAG, "Invalid uri: " + intentDescription);
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001864 itemsToRemove.add(id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001865 continue;
1866 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001867
Sunny Goyal34b65272015-03-11 16:56:52 -07001868 boolean useLowResIcon = container >= 0 &&
1869 c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
1870
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001871 if (itemReplaced) {
1872 if (user.equals(UserHandleCompat.myUserHandle())) {
Sunny Goyald09c3702016-04-06 16:18:20 -07001873 info = getAppShortcutInfo(intent, user, context, null,
Sunny Goyal4e5cc642015-06-25 16:37:44 -07001874 cursorIconInfo.iconIndex, titleIndex,
1875 false, useLowResIcon);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08001876 } else {
1877 // Don't replace items for other profiles.
1878 itemsToRemove.add(id);
1879 continue;
1880 }
1881 } else if (restored) {
Kenny Guyed131872014-04-30 03:02:21 +01001882 if (user.equals(UserHandleCompat.myUserHandle())) {
Sunny Goyal34b65272015-03-11 16:56:52 -07001883 info = getRestoredItemInfo(c, titleIndex, intent,
Sunny Goyalc22841b2015-07-13 19:59:50 -07001884 promiseType, itemType, cursorIconInfo, context);
Kenny Guyed131872014-04-30 03:02:21 +01001885 intent = getRestoredItemIntent(c, context, intent);
1886 } else {
1887 // Don't restore items for other profiles.
1888 itemsToRemove.add(id);
1889 continue;
1890 }
Chris Wrenf4d08112014-01-16 18:13:56 -05001891 } else if (itemType ==
1892 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
Sunny Goyald09c3702016-04-06 16:18:20 -07001893 info = getAppShortcutInfo(intent, user, context, c,
Sunny Goyal4e5cc642015-06-25 16:37:44 -07001894 cursorIconInfo.iconIndex, titleIndex,
1895 allowMissingTarget, useLowResIcon);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001896 } else {
Sunny Goyal4e5cc642015-06-25 16:37:44 -07001897 info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
Michael Jurka96879562012-03-22 05:54:33 -07001898
Sunny Goyald09c3702016-04-06 16:18:20 -07001899 // Shortcuts are only available on the primary profile
1900 if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
1901 disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
1902 }
1903
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001904 // App shortcuts that used to be automatically added to Launcher
1905 // didn't always have the correct intent flags set, so do that
1906 // here
1907 if (intent.getAction() != null &&
Michael Jurka9ad00562012-05-14 12:24:22 -07001908 intent.getCategories() != null &&
1909 intent.getAction().equals(Intent.ACTION_MAIN) &&
Michael Jurka96879562012-03-22 05:54:33 -07001910 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001911 intent.addFlags(
1912 Intent.FLAG_ACTIVITY_NEW_TASK |
1913 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1914 }
Michael Jurka96879562012-03-22 05:54:33 -07001915 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04001916
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001917 if (info != null) {
Winson Chungee055712013-07-30 14:46:24 -07001918 info.id = id;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001919 info.intent = intent;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001920 info.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07001921 info.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001922 info.cellX = c.getInt(cellXIndex);
1923 info.cellY = c.getInt(cellYIndex);
Sunny Goyal08f72612015-01-05 13:41:43 -08001924 info.rank = c.getInt(rankIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07001925 info.spanX = 1;
1926 info.spanY = 1;
Kenny Guyed131872014-04-30 03:02:21 +01001927 info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
Sunny Goyal4e5cc642015-06-25 16:37:44 -07001928 if (info.promisedIntent != null) {
1929 info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
1930 }
Sunny Goyald09c3702016-04-06 16:18:20 -07001931 info.isDisabled |= disabledState;
Sunny Goyal1a745e82014-10-02 15:58:31 -07001932 if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
1933 info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
1934 }
Adam Cohenae4409d2013-11-26 10:34:59 -08001935
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001936 // check & update map of what's occupied
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001937 if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
Sunny Goyalfc0fe6b2014-10-16 12:18:37 -07001938 itemsToRemove.add(id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001939 break;
1940 }
1941
Sunny Goyal756adbc2015-04-16 15:20:51 -07001942 if (restored) {
1943 ComponentName cn = info.getTargetComponent();
1944 if (cn != null) {
1945 Integer progress = installingPkgs.get(cn.getPackageName());
1946 if (progress != null) {
1947 info.setInstallProgress(progress);
1948 } else {
1949 info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
1950 }
1951 }
1952 }
1953
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001954 switch (container) {
1955 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
1956 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
1957 sBgWorkspaceItems.add(info);
1958 break;
1959 default:
1960 // Item is in a user folder
1961 FolderInfo folderInfo =
1962 findOrMakeFolder(sBgFolders, container);
Sunny Goyalc52ba712016-04-05 15:59:05 -07001963 folderInfo.add(info, false);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001964 break;
1965 }
1966 sBgItemsIdMap.put(info.id, info);
Winson Chung1323b482013-08-05 12:41:55 -07001967 } else {
1968 throw new RuntimeException("Unexpected null ShortcutInfo");
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001969 }
1970 break;
1971
1972 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
1973 id = c.getLong(idIndex);
1974 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
1975
Sunny Goyala508e4f2015-05-21 09:33:57 -07001976 // Do not trim the folder label, as is was set by the user.
1977 folderInfo.title = c.getString(titleIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001978 folderInfo.id = id;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001979 folderInfo.container = container;
Adam Cohendcd297f2013-06-18 13:13:40 -07001980 folderInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001981 folderInfo.cellX = c.getInt(cellXIndex);
1982 folderInfo.cellY = c.getInt(cellYIndex);
Winson Chung5f8afe62013-08-12 16:19:28 -07001983 folderInfo.spanX = 1;
1984 folderInfo.spanY = 1;
Sunny Goyal5d85c442015-03-10 13:14:47 -07001985 folderInfo.options = c.getInt(optionsIndex);
Joe Onorato9c1289c2009-08-17 11:03:03 -04001986
Daniel Sandler8802e962010-05-26 16:28:16 -04001987 // check & update map of what's occupied
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07001988 if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
Sunny Goyalfc0fe6b2014-10-16 12:18:37 -07001989 itemsToRemove.add(id);
Daniel Sandler8802e962010-05-26 16:28:16 -04001990 break;
1991 }
Winson Chung5f8afe62013-08-12 16:19:28 -07001992
Joe Onorato9c1289c2009-08-17 11:03:03 -04001993 switch (container) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001994 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
1995 case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
1996 sBgWorkspaceItems.add(folderInfo);
1997 break;
Joe Onorato36115782010-06-17 13:28:48 -04001998 }
Joe Onorato17a89222011-02-08 17:26:11 -08001999
Chris Wrenf4d08112014-01-16 18:13:56 -05002000 if (restored) {
2001 // no special handling required for restored folders
2002 restoredRows.add(id);
2003 }
2004
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002005 sBgItemsIdMap.put(folderInfo.id, folderInfo);
2006 sBgFolders.put(folderInfo.id, folderInfo);
2007 break;
2008
2009 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohen59400422014-03-05 18:07:04 -08002010 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002011 // Read all Launcher-specific widget details
Adam Cohen59400422014-03-05 18:07:04 -08002012 boolean customWidget = itemType ==
2013 LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
2014
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002015 int appWidgetId = c.getInt(appWidgetIdIndex);
Sunny Goyal7f834d22015-04-21 10:10:23 -07002016 serialNumber = c.getLong(profileIdIndex);
Chris Wrenc3919c02013-09-18 09:48:33 -04002017 String savedProvider = c.getString(appWidgetProviderIndex);
Joe Onorato36115782010-06-17 13:28:48 -04002018 id = c.getLong(idIndex);
Sunny Goyal7f834d22015-04-21 10:10:23 -07002019 user = allUsers.get(serialNumber);
2020 if (user == null) {
2021 itemsToRemove.add(id);
2022 continue;
2023 }
2024
Sunny Goyalff572272014-07-23 13:58:07 -07002025 final ComponentName component =
2026 ComponentName.unflattenFromString(savedProvider);
Joe Onorato36115782010-06-17 13:28:48 -04002027
Sunny Goyal651077b2014-06-30 14:15:31 -07002028 final int restoreStatus = c.getInt(restoredIndex);
Sunny Goyalff572272014-07-23 13:58:07 -07002029 final boolean isIdValid = (restoreStatus &
2030 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
Sunny Goyalff572272014-07-23 13:58:07 -07002031 final boolean wasProviderReady = (restoreStatus &
2032 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
Sunny Goyal651077b2014-06-30 14:15:31 -07002033
Sunny Goyal2e1efb42016-03-03 16:58:55 -08002034 if (widgetProvidersMap == null) {
2035 widgetProvidersMap = AppWidgetManagerCompat
2036 .getInstance(mContext).getAllProvidersMap();
2037 }
2038 final AppWidgetProviderInfo provider = widgetProvidersMap.get(
2039 new ComponentKey(
Robin Lee26ace122015-03-16 19:41:43 +00002040 ComponentName.unflattenFromString(savedProvider),
Sunny Goyal2e1efb42016-03-03 16:58:55 -08002041 user));
Sunny Goyalff572272014-07-23 13:58:07 -07002042
2043 final boolean isProviderReady = isValidProvider(provider);
Adam Cohen59400422014-03-05 18:07:04 -08002044 if (!isSafeMode && !customWidget &&
2045 wasProviderReady && !isProviderReady) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07002046 FileLog.d(TAG, "Deleting widget that isn't installed anymore: "
Sunny Goyala1365452015-10-01 15:46:24 -07002047 + provider);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002048 itemsToRemove.add(id);
2049 } else {
Sunny Goyalff572272014-07-23 13:58:07 -07002050 if (isProviderReady) {
Sunny Goyal651077b2014-06-30 14:15:31 -07002051 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
2052 provider.provider);
Adam Cohen59400422014-03-05 18:07:04 -08002053
Sunny Goyal53f96722015-07-13 19:54:53 -07002054 // The provider is available. So the widget is either
2055 // available or not available. We do not need to track
2056 // any future restore updates.
2057 int status = restoreStatus &
2058 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
Sunny Goyalff572272014-07-23 13:58:07 -07002059 if (!wasProviderReady) {
2060 // If provider was not previously ready, update the
2061 // status and UI flag.
2062
2063 // Id would be valid only if the widget restore broadcast was received.
2064 if (isIdValid) {
Sunny Goyal84b4adc2015-08-12 15:12:16 -07002065 status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
Sunny Goyalff572272014-07-23 13:58:07 -07002066 } else {
2067 status &= ~LauncherAppWidgetInfo
2068 .FLAG_PROVIDER_NOT_READY;
2069 }
2070 }
2071 appWidgetInfo.restoreStatus = status;
Sunny Goyal651077b2014-06-30 14:15:31 -07002072 } else {
2073 Log.v(TAG, "Widget restore pending id=" + id
2074 + " appWidgetId=" + appWidgetId
2075 + " status =" + restoreStatus);
2076 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
Sunny Goyalff572272014-07-23 13:58:07 -07002077 component);
Sunny Goyal651077b2014-06-30 14:15:31 -07002078 appWidgetInfo.restoreStatus = restoreStatus;
Sunny Goyal756adbc2015-04-16 15:20:51 -07002079 Integer installProgress = installingPkgs.get(component.getPackageName());
Sunny Goyal94485362014-09-18 16:13:58 -07002080
2081 if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
2082 // Restore has started once.
Sunny Goyal756adbc2015-04-16 15:20:51 -07002083 } else if (installProgress != null) {
Sunny Goyal94485362014-09-18 16:13:58 -07002084 // App restore has started. Update the flag
2085 appWidgetInfo.restoreStatus |=
2086 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
Sunny Goyal9b4b0812014-10-08 10:47:28 -07002087 } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
Sunny Goyal713edfc2016-05-06 09:58:34 -07002088 FileLog.d(TAG, "Unrestored widget removed: " + component);
Sunny Goyal94485362014-09-18 16:13:58 -07002089 itemsToRemove.add(id);
2090 continue;
2091 }
Sunny Goyal756adbc2015-04-16 15:20:51 -07002092
2093 appWidgetInfo.installProgress =
2094 installProgress == null ? 0 : installProgress;
Sunny Goyal651077b2014-06-30 14:15:31 -07002095 }
Sunny Goyalff572272014-07-23 13:58:07 -07002096
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002097 appWidgetInfo.id = id;
Adam Cohendcd297f2013-06-18 13:13:40 -07002098 appWidgetInfo.screenId = c.getInt(screenIndex);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002099 appWidgetInfo.cellX = c.getInt(cellXIndex);
2100 appWidgetInfo.cellY = c.getInt(cellYIndex);
2101 appWidgetInfo.spanX = c.getInt(spanXIndex);
2102 appWidgetInfo.spanY = c.getInt(spanYIndex);
Sunny Goyalab7a4fe2015-07-15 17:20:54 -07002103 appWidgetInfo.user = user;
Joe Onorato36115782010-06-17 13:28:48 -04002104
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002105 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2106 container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2107 Log.e(TAG, "Widget found where container != " +
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07002108 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
2109 itemsToRemove.add(id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002110 continue;
2111 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002112
Sunny Goyalb1622cc2015-06-10 16:00:42 -07002113 appWidgetInfo.container = container;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002114 // check & update map of what's occupied
Sunny Goyal41cdc8d2015-09-04 12:53:04 -07002115 if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
Sunny Goyalfc0fe6b2014-10-16 12:18:37 -07002116 itemsToRemove.add(id);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002117 break;
2118 }
Sunny Goyal651077b2014-06-30 14:15:31 -07002119
Adam Cohen59400422014-03-05 18:07:04 -08002120 if (!customWidget) {
2121 String providerName =
2122 appWidgetInfo.providerName.flattenToString();
2123 if (!providerName.equals(savedProvider) ||
2124 (appWidgetInfo.restoreStatus != restoreStatus)) {
2125 ContentValues values = new ContentValues();
2126 values.put(
2127 LauncherSettings.Favorites.APPWIDGET_PROVIDER,
2128 providerName);
2129 values.put(LauncherSettings.Favorites.RESTORED,
2130 appWidgetInfo.restoreStatus);
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08002131 updateItem(id, values);
Adam Cohen59400422014-03-05 18:07:04 -08002132 }
Chris Wrenc3919c02013-09-18 09:48:33 -04002133 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002134 sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
2135 sBgAppWidgets.add(appWidgetInfo);
2136 }
Joe Onorato36115782010-06-17 13:28:48 -04002137 break;
2138 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002139 } catch (Exception e) {
Sunny Goyala1365452015-10-01 15:46:24 -07002140 Log.e(TAG, "Desktop items loading interrupted", e);
Romain Guy5c16f3e2010-01-12 17:24:58 -08002141 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002142 }
2143 } finally {
Sunny Goyal713edfc2016-05-06 09:58:34 -07002144 Utilities.closeSilently(c);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002145 }
2146
Winson Chungba9c37f2013-08-30 14:11:37 -07002147 // Break early if we've stopped loading
2148 if (mStopped) {
Winson Chungba9c37f2013-08-30 14:11:37 -07002149 clearSBgDataStructures();
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002150 return;
Winson Chungba9c37f2013-08-30 14:11:37 -07002151 }
2152
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002153 if (itemsToRemove.size() > 0) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002154 // Remove dead items
Sunny Goyalb1622cc2015-06-10 16:00:42 -07002155 contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
2156 Utilities.createDbSelectionQuery(
2157 LauncherSettings.Favorites._ID, itemsToRemove), null);
2158 if (DEBUG_LOADERS) {
2159 Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
2160 LauncherSettings.Favorites._ID, itemsToRemove));
2161 }
2162
2163 // Remove any empty folder
Sunny Goyald2497482015-09-22 18:24:19 -07002164 ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
2165 .call(contentResolver,
2166 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
2167 .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
2168 for (long folderId : deletedFolderIds) {
Sunny Goyalb1622cc2015-06-10 16:00:42 -07002169 sBgWorkspaceItems.remove(sBgFolders.get(folderId));
2170 sBgFolders.remove(folderId);
2171 sBgItemsIdMap.remove(folderId);
Romain Guy5c16f3e2010-01-12 17:24:58 -08002172 }
2173 }
2174
Sunny Goyal317698b2015-07-29 11:45:41 -07002175 // Sort all the folder items and make sure the first 3 items are high resolution.
2176 for (FolderInfo folder : sBgFolders) {
2177 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
2178 int pos = 0;
2179 for (ShortcutInfo info : folder.contents) {
2180 if (info.usingLowResIcon) {
2181 info.updateIcon(mIconCache, false);
2182 }
2183 pos ++;
2184 if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
2185 break;
2186 }
2187 }
2188 }
2189
Chris Wrenf4d08112014-01-16 18:13:56 -05002190 if (restoredRows.size() > 0) {
Chris Wrenf4d08112014-01-16 18:13:56 -05002191 // Update restored items that no longer require special handling
Sunny Goyalb1622cc2015-06-10 16:00:42 -07002192 ContentValues values = new ContentValues();
2193 values.put(LauncherSettings.Favorites.RESTORED, 0);
2194 contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
2195 Utilities.createDbSelectionQuery(
2196 LauncherSettings.Favorites._ID, restoredRows), null);
Chris Wrenf4d08112014-01-16 18:13:56 -05002197 }
2198
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002199 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
2200 context.registerReceiver(new AppsAvailabilityCheck(),
Sunny Goyal25aba0a2015-07-16 15:07:47 -07002201 new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002202 null, sWorker);
2203 }
2204
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002205 // Remove any empty screens
2206 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
Sunny Goyale2df0622015-04-24 11:27:00 -07002207 for (ItemInfo item: sBgItemsIdMap) {
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002208 long screenId = item.screenId;
2209 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
2210 unusedScreens.contains(screenId)) {
2211 unusedScreens.remove(screenId);
2212 }
2213 }
2214
2215 // If there are any empty screens remove them, and update.
2216 if (unusedScreens.size() != 0) {
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002217 sBgWorkspaceScreens.removeAll(unusedScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002218 updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
Adam Cohendcd297f2013-06-18 13:13:40 -07002219 }
2220
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002221 if (DEBUG_LOADERS) {
2222 Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
2223 Log.d(TAG, "workspace layout: ");
Adam Cohendcd297f2013-06-18 13:13:40 -07002224 int nScreens = occupied.size();
Winson Chung892c74d2013-08-22 16:15:50 -07002225 for (int y = 0; y < countY; y++) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002226 String line = "";
Adam Cohendcd297f2013-06-18 13:13:40 -07002227
Sunny Goyale2df0622015-04-24 11:27:00 -07002228 for (int i = 0; i < nScreens; i++) {
2229 long screenId = occupied.keyAt(i);
Winson Chungc9168342013-06-26 14:54:55 -07002230 if (screenId > 0) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002231 line += " | ";
2232 }
Sunny Goyale2df0622015-04-24 11:27:00 -07002233 ItemInfo[][] screen = occupied.valueAt(i);
Winson Chung892c74d2013-08-22 16:15:50 -07002234 for (int x = 0; x < countX; x++) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002235 if (x < screen.length && y < screen[x].length) {
2236 line += (screen[x][y] != null) ? "#" : ".";
2237 } else {
2238 line += "!";
2239 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002240 }
Joe Onorato36115782010-06-17 13:28:48 -04002241 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002242 Log.d(TAG, "[ " + line + " ]");
Joe Onorato36115782010-06-17 13:28:48 -04002243 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04002244 }
Joe Onorato36115782010-06-17 13:28:48 -04002245 }
Adam Cohene25af792013-06-06 23:08:25 -07002246 }
2247
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08002248 /**
2249 * Partially updates the item without any notification. Must be called on the worker thread.
2250 */
2251 private void updateItem(long itemId, ContentValues update) {
2252 mContext.getContentResolver().update(
Sunny Goyal1d4a2df2015-03-30 11:11:46 -07002253 LauncherSettings.Favorites.CONTENT_URI,
Sunny Goyalbb3b02f2015-01-15 12:00:14 -08002254 update,
2255 BaseColumns._ID + "= ?",
2256 new String[]{Long.toString(itemId)});
2257 }
2258
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002259 /** Filters the set of items who are directly or indirectly (via another container) on the
2260 * specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002261 private void filterCurrentWorkspaceItems(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002262 ArrayList<ItemInfo> allWorkspaceItems,
2263 ArrayList<ItemInfo> currentScreenItems,
2264 ArrayList<ItemInfo> otherScreenItems) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002265 // Purge any null ItemInfos
2266 Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
2267 while (iter.hasNext()) {
2268 ItemInfo i = iter.next();
2269 if (i == null) {
2270 iter.remove();
2271 }
2272 }
2273
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002274 // Order the set of items by their containers first, this allows use to walk through the
2275 // list sequentially, build up a list of containers that are in the specified screen,
2276 // as well as all items in those containers.
2277 Set<Long> itemsOnScreen = new HashSet<Long>();
2278 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
2279 @Override
2280 public int compare(ItemInfo lhs, ItemInfo rhs) {
Winson12fb9fc2015-10-01 15:34:08 -07002281 return Utilities.longCompare(lhs.container, rhs.container);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002282 }
2283 });
2284 for (ItemInfo info : allWorkspaceItems) {
2285 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Winson Chung9b9fb962013-11-15 15:39:34 -08002286 if (info.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002287 currentScreenItems.add(info);
2288 itemsOnScreen.add(info.id);
2289 } else {
2290 otherScreenItems.add(info);
2291 }
2292 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
2293 currentScreenItems.add(info);
2294 itemsOnScreen.add(info.id);
2295 } else {
2296 if (itemsOnScreen.contains(info.container)) {
2297 currentScreenItems.add(info);
2298 itemsOnScreen.add(info.id);
2299 } else {
2300 otherScreenItems.add(info);
2301 }
2302 }
2303 }
2304 }
2305
2306 /** Filters the set of widgets which are on the specified screen. */
Winson Chung9b9fb962013-11-15 15:39:34 -08002307 private void filterCurrentAppWidgets(long currentScreenId,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002308 ArrayList<LauncherAppWidgetInfo> appWidgets,
2309 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
2310 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002311
2312 for (LauncherAppWidgetInfo widget : appWidgets) {
Winson Chung2abf94d2012-07-18 18:16:38 -07002313 if (widget == null) continue;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002314 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
Winson Chung9b9fb962013-11-15 15:39:34 -08002315 widget.screenId == currentScreenId) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002316 currentScreenWidgets.add(widget);
2317 } else {
2318 otherScreenWidgets.add(widget);
2319 }
2320 }
2321 }
2322
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002323 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
2324 * right) */
2325 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
Winson Chung892c74d2013-08-22 16:15:50 -07002326 final LauncherAppState app = LauncherAppState.getInstance();
Adam Cohen2e6da152015-05-06 11:42:25 -07002327 final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
Winson Chung882a52e2015-07-08 14:32:26 -07002328 final int screenCols = profile.numColumns;
2329 final int screenCellCount = profile.numColumns * profile.numRows;
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002330 Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
Winson Chungdb8a8942012-04-03 14:08:41 -07002331 @Override
2332 public int compare(ItemInfo lhs, ItemInfo rhs) {
Winson Chung882a52e2015-07-08 14:32:26 -07002333 if (lhs.container == rhs.container) {
2334 // Within containers, order by their spatial position in that container
2335 switch ((int) lhs.container) {
2336 case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
2337 long lr = (lhs.screenId * screenCellCount +
2338 lhs.cellY * screenCols + lhs.cellX);
2339 long rr = (rhs.screenId * screenCellCount +
2340 rhs.cellY * screenCols + rhs.cellX);
Winson722e8562015-10-07 13:04:30 -07002341 return Utilities.longCompare(lr, rr);
Winson Chung882a52e2015-07-08 14:32:26 -07002342 }
2343 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
2344 // We currently use the screen id as the rank
Winson722e8562015-10-07 13:04:30 -07002345 return Utilities.longCompare(lhs.screenId, rhs.screenId);
Winson Chung882a52e2015-07-08 14:32:26 -07002346 }
2347 default:
Sunny Goyal6c56c682015-07-16 14:09:05 -07002348 if (ProviderConfig.IS_DOGFOOD_BUILD) {
Winson Chung882a52e2015-07-08 14:32:26 -07002349 throw new RuntimeException("Unexpected container type when " +
2350 "sorting workspace items.");
2351 }
2352 return 0;
2353 }
2354 } else {
2355 // Between containers, order by hotseat, desktop
Winson722e8562015-10-07 13:04:30 -07002356 return Utilities.longCompare(lhs.container, rhs.container);
Winson Chung882a52e2015-07-08 14:32:26 -07002357 }
Winson Chungdb8a8942012-04-03 14:08:41 -07002358 }
2359 });
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002360 }
Winson Chungdb8a8942012-04-03 14:08:41 -07002361
Adam Cohendcd297f2013-06-18 13:13:40 -07002362 private void bindWorkspaceScreens(final Callbacks oldCallbacks,
2363 final ArrayList<Long> orderedScreens) {
Adam Cohendcd297f2013-06-18 13:13:40 -07002364 final Runnable r = new Runnable() {
2365 @Override
2366 public void run() {
2367 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2368 if (callbacks != null) {
2369 callbacks.bindScreens(orderedScreens);
2370 }
2371 }
2372 };
Sunny Goyald33860f2015-04-23 16:02:20 -07002373 runOnMainThread(r);
Adam Cohendcd297f2013-06-18 13:13:40 -07002374 }
2375
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002376 private void bindWorkspaceItems(final Callbacks oldCallbacks,
2377 final ArrayList<ItemInfo> workspaceItems,
2378 final ArrayList<LauncherAppWidgetInfo> appWidgets,
Sunny Goyal527c7d32015-08-28 15:19:36 -07002379 final Executor executor) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002380
2381 // Bind the workspace items
Winson Chungdb8a8942012-04-03 14:08:41 -07002382 int N = workspaceItems.size();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002383 for (int i = 0; i < N; i += ITEMS_CHUNK) {
Joe Onorato36115782010-06-17 13:28:48 -04002384 final int start = i;
2385 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002386 final Runnable r = new Runnable() {
2387 @Override
Joe Onorato9c1289c2009-08-17 11:03:03 -04002388 public void run() {
Joe Onoratoc131b742010-03-11 15:45:05 -08002389 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002390 if (callbacks != null) {
Winson Chung64359a52013-07-08 17:17:08 -07002391 callbacks.bindItems(workspaceItems, start, start+chunkSize,
2392 false);
Joe Onorato9c1289c2009-08-17 11:03:03 -04002393 }
2394 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002395 };
Sunny Goyal527c7d32015-08-28 15:19:36 -07002396 executor.execute(r);
Joe Onorato36115782010-06-17 13:28:48 -04002397 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002398
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002399 // Bind the widgets, one at a time
2400 N = appWidgets.size();
2401 for (int i = 0; i < N; i++) {
2402 final LauncherAppWidgetInfo widget = appWidgets.get(i);
2403 final Runnable r = new Runnable() {
2404 public void run() {
2405 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2406 if (callbacks != null) {
2407 callbacks.bindAppWidget(widget);
2408 }
2409 }
2410 };
Sunny Goyal527c7d32015-08-28 15:19:36 -07002411 executor.execute(r);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002412 }
2413 }
2414
2415 /**
2416 * Binds all loaded data to actual views on the main thread.
2417 */
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002418 private void bindWorkspace(int synchronizeBindPage) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002419 final long t = SystemClock.uptimeMillis();
2420 Runnable r;
2421
2422 // Don't use these two variables in any of the callback runnables.
2423 // Otherwise we hold a reference to them.
2424 final Callbacks oldCallbacks = mCallbacks.get();
2425 if (oldCallbacks == null) {
2426 // This launcher has exited and nobody bothered to tell us. Just bail.
2427 Log.w(TAG, "LoaderTask running with no launcher");
2428 return;
2429 }
2430
Winson Chung9b9fb962013-11-15 15:39:34 -08002431 // Save a copy of all the bg-thread collections
Sunny Goyal44c06432016-04-02 10:56:02 -07002432 ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
2433 ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
2434 ArrayList<Long> orderedScreenIds = new ArrayList<>();
Sunny Goyale2df0622015-04-24 11:27:00 -07002435
Winson Chung2abf94d2012-07-18 18:16:38 -07002436 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002437 workspaceItems.addAll(sBgWorkspaceItems);
2438 appWidgets.addAll(sBgAppWidgets);
Adam Cohendcd297f2013-06-18 13:13:40 -07002439 orderedScreenIds.addAll(sBgWorkspaceScreens);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002440 }
2441
Derek Prothro7aff3992013-12-10 14:00:37 -05002442 final boolean isLoadingSynchronously =
2443 synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
Adam Cohend8dbb462013-11-27 11:55:48 -08002444 int currScreen = isLoadingSynchronously ? synchronizeBindPage :
Winson Chung9b9fb962013-11-15 15:39:34 -08002445 oldCallbacks.getCurrentWorkspaceScreen();
Adam Cohend8dbb462013-11-27 11:55:48 -08002446 if (currScreen >= orderedScreenIds.size()) {
2447 // There may be no workspace screens (just hotseat items and an empty page).
Derek Prothro7aff3992013-12-10 14:00:37 -05002448 currScreen = PagedView.INVALID_RESTORE_PAGE;
Winson Chung9b9fb962013-11-15 15:39:34 -08002449 }
Adam Cohend8dbb462013-11-27 11:55:48 -08002450 final int currentScreen = currScreen;
Derek Prothro7aff3992013-12-10 14:00:37 -05002451 final long currentScreenId = currentScreen < 0
2452 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
Winson Chung9b9fb962013-11-15 15:39:34 -08002453
Winson Chung9b9fb962013-11-15 15:39:34 -08002454 // Separate the items that are on the current screen, and all the other remaining items
Sunny Goyal44c06432016-04-02 10:56:02 -07002455 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
2456 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
2457 ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
2458 ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002459
Winson Chung9b9fb962013-11-15 15:39:34 -08002460 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002461 otherWorkspaceItems);
Winson Chung9b9fb962013-11-15 15:39:34 -08002462 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002463 otherAppWidgets);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002464 sortWorkspaceItemsSpatially(currentWorkspaceItems);
2465 sortWorkspaceItemsSpatially(otherWorkspaceItems);
2466
2467 // Tell the workspace that we're about to start binding items
2468 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002469 public void run() {
2470 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2471 if (callbacks != null) {
Sunny Goyal527c7d32015-08-28 15:19:36 -07002472 callbacks.clearPendingBinds();
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002473 callbacks.startBinding();
Joe Onorato36115782010-06-17 13:28:48 -04002474 }
2475 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002476 };
Sunny Goyald33860f2015-04-23 16:02:20 -07002477 runOnMainThread(r);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002478
Adam Cohendcd297f2013-06-18 13:13:40 -07002479 bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
2480
Sunny Goyal527c7d32015-08-28 15:19:36 -07002481 Executor mainExecutor = new DeferredMainThreadExecutor();
2482 // Load items on the current page.
Sunny Goyal44c06432016-04-02 10:56:02 -07002483 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002484
Sunny Goyal527c7d32015-08-28 15:19:36 -07002485 // In case of isLoadingSynchronously, only bind the first screen, and defer binding the
2486 // remaining screens after first onDraw is called. This ensures that the first screen
2487 // is immediately visible (eg. during rotation)
2488 // In case of !isLoadingSynchronously, bind all pages one after other.
2489 final Executor deferredExecutor = isLoadingSynchronously ?
2490 new ViewOnDrawExecutor(mHandler) : mainExecutor;
2491
Sunny Goyal44c06432016-04-02 10:56:02 -07002492 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002493
2494 // Tell the workspace that we're done binding items
2495 r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002496 public void run() {
2497 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2498 if (callbacks != null) {
Sunny Goyal66cfdc22015-02-02 13:01:51 -08002499 callbacks.finishBindingItems();
Joe Onorato36115782010-06-17 13:28:48 -04002500 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002501
Sunny Goyal639e9062015-08-19 19:17:06 -07002502 mIsLoadingAndBindingWorkspace = false;
2503
2504 // Run all the bind complete runnables after workspace is bound.
2505 if (!mBindCompleteRunnables.isEmpty()) {
2506 synchronized (mBindCompleteRunnables) {
2507 for (final Runnable r : mBindCompleteRunnables) {
2508 runOnWorkerThread(r);
2509 }
2510 mBindCompleteRunnables.clear();
2511 }
2512 }
2513
Winson Chung98e030b2012-05-07 16:01:11 -07002514 // If we're profiling, ensure this is the last thing in the queue.
Joe Onorato36115782010-06-17 13:28:48 -04002515 if (DEBUG_LOADERS) {
2516 Log.d(TAG, "bound workspace in "
2517 + (SystemClock.uptimeMillis()-t) + "ms");
2518 }
Winson Chung36a62fe2012-05-06 18:04:42 -07002519
Joe Onorato36115782010-06-17 13:28:48 -04002520 }
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002521 };
Sunny Goyal527c7d32015-08-28 15:19:36 -07002522 deferredExecutor.execute(r);
2523
Winson Chung4a2afa32012-07-19 14:53:05 -07002524 if (isLoadingSynchronously) {
Sunny Goyal527c7d32015-08-28 15:19:36 -07002525 r = new Runnable() {
2526 public void run() {
2527 Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2528 if (callbacks != null) {
2529 // We are loading synchronously, which means, some of the pages will be
2530 // bound after first draw. Inform the callbacks that page binding is
2531 // not complete, and schedule the remaining pages.
2532 if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
2533 callbacks.onPageBoundSynchronously(currentScreen);
2534 }
2535 callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
2536 }
2537 }
2538 };
Sunny Goyald33860f2015-04-23 16:02:20 -07002539 runOnMainThread(r);
Winson Chung4a2afa32012-07-19 14:53:05 -07002540 }
Joe Onorato36115782010-06-17 13:28:48 -04002541 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002542
Joe Onorato36115782010-06-17 13:28:48 -04002543 private void loadAndBindAllApps() {
2544 if (DEBUG_LOADERS) {
2545 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
2546 }
2547 if (!mAllAppsLoaded) {
Winson Chung64359a52013-07-08 17:17:08 -07002548 loadAllApps();
Sunny Goyalf5cd9982015-05-18 15:19:29 -07002549 synchronized (LoaderTask.this) {
2550 if (mStopped) {
2551 return;
2552 }
2553 }
Sunny Goyal4e5cc642015-06-25 16:37:44 -07002554 updateIconCache();
Reena Lee93f824a2011-09-23 17:20:28 -07002555 synchronized (LoaderTask.this) {
2556 if (mStopped) {
2557 return;
2558 }
2559 mAllAppsLoaded = true;
Joe Onoratocc67f472010-06-08 10:54:30 -07002560 }
Joe Onorato36115782010-06-17 13:28:48 -04002561 } else {
2562 onlyBindAllApps();
2563 }
2564 }
Joe Onoratocc67f472010-06-08 10:54:30 -07002565
Sunny Goyal4e5cc642015-06-25 16:37:44 -07002566 private void updateIconCache() {
2567 // Ignore packages which have a promise icon.
2568 HashSet<String> packagesToIgnore = new HashSet<>();
2569 synchronized (sBgLock) {
2570 for (ItemInfo info : sBgItemsIdMap) {
2571 if (info instanceof ShortcutInfo) {
2572 ShortcutInfo si = (ShortcutInfo) info;
2573 if (si.isPromise() && si.getTargetComponent() != null) {
2574 packagesToIgnore.add(si.getTargetComponent().getPackageName());
2575 }
2576 } else if (info instanceof LauncherAppWidgetInfo) {
2577 LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
2578 if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
2579 packagesToIgnore.add(lawi.providerName.getPackageName());
2580 }
2581 }
2582 }
2583 }
2584 mIconCache.updateDbIcons(packagesToIgnore);
2585 }
2586
Joe Onorato36115782010-06-17 13:28:48 -04002587 private void onlyBindAllApps() {
2588 final Callbacks oldCallbacks = mCallbacks.get();
2589 if (oldCallbacks == null) {
2590 // This launcher has exited and nobody bothered to tell us. Just bail.
2591 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
2592 return;
2593 }
2594
2595 // shallow copy
Winson Chungc208ff92012-03-29 17:37:41 -07002596 @SuppressWarnings("unchecked")
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002597 final ArrayList<AppInfo> list
2598 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
Winson Chungc93e5ae2012-07-23 20:48:26 -07002599 Runnable r = new Runnable() {
Joe Onorato36115782010-06-17 13:28:48 -04002600 public void run() {
2601 final long t = SystemClock.uptimeMillis();
2602 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
2603 if (callbacks != null) {
2604 callbacks.bindAllApplications(list);
2605 }
2606 if (DEBUG_LOADERS) {
2607 Log.d(TAG, "bound all " + list.size() + " apps from cache in "
Hyunyoung Song747a5bc2016-02-08 11:31:33 -08002608 + (SystemClock.uptimeMillis() - t) + "ms");
Joe Onorato36115782010-06-17 13:28:48 -04002609 }
2610 }
Winson Chungc93e5ae2012-07-23 20:48:26 -07002611 };
2612 boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
Winson Chung64359a52013-07-08 17:17:08 -07002613 if (isRunningOnMainThread) {
Winson Chungc93e5ae2012-07-23 20:48:26 -07002614 r.run();
2615 } else {
2616 mHandler.post(r);
2617 }
Joe Onorato36115782010-06-17 13:28:48 -04002618 }
2619
Winson Chung64359a52013-07-08 17:17:08 -07002620 private void loadAllApps() {
2621 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Joe Onorato36115782010-06-17 13:28:48 -04002622
Joe Onorato36115782010-06-17 13:28:48 -04002623 final Callbacks oldCallbacks = mCallbacks.get();
2624 if (oldCallbacks == null) {
2625 // This launcher has exited and nobody bothered to tell us. Just bail.
Winson Chung64359a52013-07-08 17:17:08 -07002626 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
Joe Onorato36115782010-06-17 13:28:48 -04002627 return;
2628 }
2629
Kenny Guyed131872014-04-30 03:02:21 +01002630 final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
2631
Winson Chung64359a52013-07-08 17:17:08 -07002632 // Clear the list of apps
2633 mBgAllAppsList.clear();
Kenny Guyed131872014-04-30 03:02:21 +01002634 for (UserHandleCompat user : profiles) {
2635 // Query for the set of apps
2636 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Sunny Goyal756a28a2015-04-23 17:07:55 -07002637 final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
Kenny Guyed131872014-04-30 03:02:21 +01002638 if (DEBUG_LOADERS) {
2639 Log.d(TAG, "getActivityList took "
2640 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
2641 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
2642 }
2643 // Fail if we don't have any apps
Sunny Goyale0f58d72014-11-10 18:05:31 -08002644 // TODO: Fix this. Only fail for the current user.
Kenny Guyed131872014-04-30 03:02:21 +01002645 if (apps == null || apps.isEmpty()) {
2646 return;
2647 }
Kenny Guyff05f432016-01-22 17:48:29 +00002648 boolean quietMode = mUserManager.isQuietModeEnabled(user);
Kenny Guyed131872014-04-30 03:02:21 +01002649 // Create the ApplicationInfos
2650 for (int i = 0; i < apps.size(); i++) {
2651 LauncherActivityInfoCompat app = apps.get(i);
2652 // This builds the icon bitmaps.
Kenny Guyff05f432016-01-22 17:48:29 +00002653 mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
Kenny Guyed131872014-04-30 03:02:21 +01002654 }
Sunny Goyale0f58d72014-11-10 18:05:31 -08002655
Sunny Goyal756a28a2015-04-23 17:07:55 -07002656 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
2657 if (heuristic != null) {
Sunny Goyal639e9062015-08-19 19:17:06 -07002658 final Runnable r = new Runnable() {
Sunny Goyal756a28a2015-04-23 17:07:55 -07002659
2660 @Override
2661 public void run() {
2662 heuristic.processUserApps(apps);
2663 }
Sunny Goyal639e9062015-08-19 19:17:06 -07002664 };
2665 runOnMainThread(new Runnable() {
2666
2667 @Override
2668 public void run() {
2669 // Check isLoadingWorkspace on the UI thread, as it is updated on
2670 // the UI thread.
2671 if (mIsLoadingAndBindingWorkspace) {
2672 synchronized (mBindCompleteRunnables) {
2673 mBindCompleteRunnables.add(r);
2674 }
2675 } else {
2676 runOnWorkerThread(r);
2677 }
2678 }
Sunny Goyal756a28a2015-04-23 17:07:55 -07002679 });
Sunny Goyale0f58d72014-11-10 18:05:31 -08002680 }
Winson Chung64359a52013-07-08 17:17:08 -07002681 }
Bjorn Bringert85f418d2013-09-06 12:50:05 +01002682 // Huh? Shouldn't this be inside the Runnable below?
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002683 final ArrayList<AppInfo> added = mBgAllAppsList.added;
2684 mBgAllAppsList.added = new ArrayList<AppInfo>();
Winson Chung64359a52013-07-08 17:17:08 -07002685
2686 // Post callback on main thread
2687 mHandler.post(new Runnable() {
2688 public void run() {
Hyunyoung Song9892e582015-05-05 10:07:23 -07002689
Winson Chung64359a52013-07-08 17:17:08 -07002690 final long bindTime = SystemClock.uptimeMillis();
Winson Chung11a1a532013-09-13 11:14:45 -07002691 final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
Winson Chung64359a52013-07-08 17:17:08 -07002692 if (callbacks != null) {
2693 callbacks.bindAllApplications(added);
2694 if (DEBUG_LOADERS) {
2695 Log.d(TAG, "bound " + added.size() + " apps in "
Sunny Goyal2e1efb42016-03-03 16:58:55 -08002696 + (SystemClock.uptimeMillis() - bindTime) + "ms");
Winson Chung64359a52013-07-08 17:17:08 -07002697 }
2698 } else {
2699 Log.i(TAG, "not binding apps: no Launcher activity");
2700 }
2701 }
2702 });
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002703 // Cleanup any data stored for a deleted user.
2704 ManagedProfileHeuristic.processAllUsers(profiles, mContext);
Joe Onorato36115782010-06-17 13:28:48 -04002705 if (DEBUG_LOADERS) {
Winson Chung64359a52013-07-08 17:17:08 -07002706 Log.d(TAG, "Icons processed in "
2707 + (SystemClock.uptimeMillis() - loadTime) + "ms");
Joe Onoratobe386092009-11-17 17:32:16 -08002708 }
2709 }
2710
2711 public void dumpState() {
Winson Chung2abf94d2012-07-18 18:16:38 -07002712 synchronized (sBgLock) {
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002713 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07002714 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
2715 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
2716 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
2717 }
Joe Onorato36115782010-06-17 13:28:48 -04002718 }
2719 }
2720
Sunny Goyal75b0f552015-05-20 21:57:06 -07002721 /**
2722 * Called when the icons for packages have been updated in the icon cache.
2723 */
2724 public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandleCompat user) {
2725 final Callbacks callbacks = getCallback();
2726 final ArrayList<AppInfo> updatedApps = new ArrayList<>();
2727 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
2728
2729 // If any package icon has changed (app was updated while launcher was dead),
2730 // update the corresponding shortcuts.
2731 synchronized (sBgLock) {
2732 for (ItemInfo info : sBgItemsIdMap) {
2733 if (info instanceof ShortcutInfo && user.equals(info.user)
2734 && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
2735 ShortcutInfo si = (ShortcutInfo) info;
2736 ComponentName cn = si.getTargetComponent();
2737 if (cn != null && updatedPackages.contains(cn.getPackageName())) {
2738 si.updateIcon(mIconCache);
2739 updatedShortcuts.add(si);
2740 }
2741 }
2742 }
2743 mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
2744 }
2745
2746 if (!updatedShortcuts.isEmpty()) {
2747 final UserHandleCompat userFinal = user;
2748 mHandler.post(new Runnable() {
2749
2750 public void run() {
2751 Callbacks cb = getCallback();
2752 if (cb != null && callbacks == cb) {
2753 cb.bindShortcutsChanged(updatedShortcuts,
2754 new ArrayList<ShortcutInfo>(), userFinal);
2755 }
2756 }
2757 });
2758 }
2759
2760 if (!updatedApps.isEmpty()) {
2761 mHandler.post(new Runnable() {
2762
2763 public void run() {
2764 Callbacks cb = getCallback();
2765 if (cb != null && callbacks == cb) {
2766 cb.bindAppsUpdated(updatedApps);
2767 }
2768 }
2769 });
2770 }
2771 }
2772
Joe Onorato36115782010-06-17 13:28:48 -04002773 void enqueuePackageUpdated(PackageUpdatedTask task) {
Brad Fitzpatrick700889f2010-10-11 09:40:44 -07002774 sWorker.post(task);
Joe Onorato36115782010-06-17 13:28:48 -04002775 }
2776
Adam Cohen091440a2015-03-18 14:16:05 -07002777 @Thunk class AppsAvailabilityCheck extends BroadcastReceiver {
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002778
2779 @Override
2780 public void onReceive(Context context, Intent intent) {
2781 synchronized (sBgLock) {
2782 final LauncherAppsCompat launcherApps = LauncherAppsCompat
2783 .getInstance(mApp.getContext());
Sunny Goyal1a745e82014-10-02 15:58:31 -07002784 final PackageManager manager = context.getPackageManager();
2785 final ArrayList<String> packagesRemoved = new ArrayList<String>();
2786 final ArrayList<String> packagesUnavailable = new ArrayList<String>();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002787 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
2788 UserHandleCompat user = entry.getKey();
Sunny Goyal1a745e82014-10-02 15:58:31 -07002789 packagesRemoved.clear();
2790 packagesUnavailable.clear();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002791 for (String pkg : entry.getValue()) {
2792 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
Sunny Goyald09c3702016-04-06 16:18:20 -07002793 if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
Sunny Goyal1a745e82014-10-02 15:58:31 -07002794 packagesUnavailable.add(pkg);
2795 } else {
Sunny Goyal1a745e82014-10-02 15:58:31 -07002796 packagesRemoved.add(pkg);
2797 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002798 }
2799 }
2800 if (!packagesRemoved.isEmpty()) {
2801 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
2802 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
2803 }
Sunny Goyal1a745e82014-10-02 15:58:31 -07002804 if (!packagesUnavailable.isEmpty()) {
2805 enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
2806 packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
2807 }
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002808 }
Sunny Goyal34942622014-08-29 17:20:55 -07002809 sPendingPackages.clear();
Sunny Goyalf599ccf2014-07-08 13:01:29 -07002810 }
2811 }
2812 }
2813
Joe Onorato36115782010-06-17 13:28:48 -04002814 private class PackageUpdatedTask implements Runnable {
2815 int mOp;
2816 String[] mPackages;
Kenny Guyed131872014-04-30 03:02:21 +01002817 UserHandleCompat mUser;
Joe Onorato36115782010-06-17 13:28:48 -04002818
2819 public static final int OP_NONE = 0;
2820 public static final int OP_ADD = 1;
2821 public static final int OP_UPDATE = 2;
2822 public static final int OP_REMOVE = 3; // uninstlled
2823 public static final int OP_UNAVAILABLE = 4; // external media unmounted
Kenny Guy44cba692016-01-21 19:50:02 +00002824 public static final int OP_SUSPEND = 5; // package suspended
2825 public static final int OP_UNSUSPEND = 6; // package unsuspended
Sunny Goyalda891c12016-03-18 18:29:24 -07002826 public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
Joe Onorato36115782010-06-17 13:28:48 -04002827
Kenny Guyed131872014-04-30 03:02:21 +01002828 public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
Joe Onorato36115782010-06-17 13:28:48 -04002829 mOp = op;
2830 mPackages = packages;
Kenny Guyed131872014-04-30 03:02:21 +01002831 mUser = user;
Joe Onorato36115782010-06-17 13:28:48 -04002832 }
2833
2834 public void run() {
Sunny Goyalc905efc2015-05-06 09:54:53 -07002835 if (!mHasLoaderCompletedOnce) {
2836 // Loader has not yet run.
2837 return;
2838 }
Daniel Sandlercc8befa2013-06-11 14:45:48 -04002839 final Context context = mApp.getContext();
Joe Onorato36115782010-06-17 13:28:48 -04002840
2841 final String[] packages = mPackages;
2842 final int N = packages.length;
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002843 FlagOp flagOp = FlagOp.NO_OP;
Sunny Goyalda891c12016-03-18 18:29:24 -07002844 StringFilter pkgFilter = StringFilter.of(new HashSet<>(Arrays.asList(packages)));
Joe Onorato36115782010-06-17 13:28:48 -04002845 switch (mOp) {
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002846 case OP_ADD: {
Joe Onorato36115782010-06-17 13:28:48 -04002847 for (int i=0; i<N; i++) {
2848 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
Sunny Goyal4fbc3822015-02-18 16:46:50 -08002849 mIconCache.updateIconsForPkg(packages[i], mUser);
Kenny Guyed131872014-04-30 03:02:21 +01002850 mBgAllAppsList.addPackage(context, packages[i], mUser);
Joe Onorato36115782010-06-17 13:28:48 -04002851 }
Sunny Goyale0f58d72014-11-10 18:05:31 -08002852
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002853 ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
2854 if (heuristic != null) {
2855 heuristic.processPackageAdd(mPackages);
Sunny Goyale0f58d72014-11-10 18:05:31 -08002856 }
Joe Onorato36115782010-06-17 13:28:48 -04002857 break;
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002858 }
Joe Onorato36115782010-06-17 13:28:48 -04002859 case OP_UPDATE:
2860 for (int i=0; i<N; i++) {
2861 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
Sunny Goyal4fbc3822015-02-18 16:46:50 -08002862 mIconCache.updateIconsForPkg(packages[i], mUser);
Kenny Guyed131872014-04-30 03:02:21 +01002863 mBgAllAppsList.updatePackage(context, packages[i], mUser);
Sunny Goyal5b0e6692015-03-19 14:31:19 -07002864 mApp.getWidgetCache().removePackage(packages[i], mUser);
Joe Onorato36115782010-06-17 13:28:48 -04002865 }
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002866 // Since package was just updated, the target must be available now.
2867 flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
Joe Onorato36115782010-06-17 13:28:48 -04002868 break;
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002869 case OP_REMOVE: {
2870 ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
2871 if (heuristic != null) {
2872 heuristic.processPackageRemoved(mPackages);
Sunny Goyale0f58d72014-11-10 18:05:31 -08002873 }
Joe Onorato36115782010-06-17 13:28:48 -04002874 for (int i=0; i<N; i++) {
2875 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
Sunny Goyal4fbc3822015-02-18 16:46:50 -08002876 mIconCache.removeIconsForPkg(packages[i], mUser);
2877 }
2878 // Fall through
Sunny Goyal18bf8e22015-04-08 18:13:46 -07002879 }
Sunny Goyal4fbc3822015-02-18 16:46:50 -08002880 case OP_UNAVAILABLE:
2881 for (int i=0; i<N; i++) {
2882 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
2883 mBgAllAppsList.removePackage(packages[i], mUser);
Sunny Goyal5b0e6692015-03-19 14:31:19 -07002884 mApp.getWidgetCache().removePackage(packages[i], mUser);
Joe Onorato36115782010-06-17 13:28:48 -04002885 }
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002886 flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
Joe Onorato36115782010-06-17 13:28:48 -04002887 break;
Kenny Guy44cba692016-01-21 19:50:02 +00002888 case OP_SUSPEND:
2889 case OP_UNSUSPEND:
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002890 flagOp = mOp == OP_SUSPEND ?
2891 FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
2892 FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
Sunny Goyal17763cb2016-03-24 13:53:22 -07002893 if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
Sunny Goyalda891c12016-03-18 18:29:24 -07002894 mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
2895 break;
2896 case OP_USER_AVAILABILITY_CHANGE:
2897 flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
2898 ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
2899 : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
2900 // We want to update all packages for this user.
2901 pkgFilter = StringFilter.matchesAll();
2902 mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
Kenny Guy44cba692016-01-21 19:50:02 +00002903 break;
Joe Onorato36115782010-06-17 13:28:48 -04002904 }
2905
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002906 ArrayList<AppInfo> added = null;
2907 ArrayList<AppInfo> modified = null;
2908 final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
Joe Onorato36115782010-06-17 13:28:48 -04002909
Adam Cohen487f7dd2012-06-28 18:12:10 -07002910 if (mBgAllAppsList.added.size() > 0) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002911 added = new ArrayList<>(mBgAllAppsList.added);
Winson Chung5d55f332012-07-16 20:45:03 -07002912 mBgAllAppsList.added.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002913 }
Adam Cohen487f7dd2012-06-28 18:12:10 -07002914 if (mBgAllAppsList.modified.size() > 0) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002915 modified = new ArrayList<>(mBgAllAppsList.modified);
Winson Chung5d55f332012-07-16 20:45:03 -07002916 mBgAllAppsList.modified.clear();
Joe Onorato36115782010-06-17 13:28:48 -04002917 }
Winson Chung5d55f332012-07-16 20:45:03 -07002918 if (mBgAllAppsList.removed.size() > 0) {
Winson Chung83892cc2013-05-01 16:53:33 -07002919 removedApps.addAll(mBgAllAppsList.removed);
Winson Chung5d55f332012-07-16 20:45:03 -07002920 mBgAllAppsList.removed.clear();
Winson Chungcd810732012-06-18 16:45:43 -07002921 }
2922
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002923 final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
Sunny Goyal4390ace2014-10-13 11:33:11 -07002924
Joe Onorato36115782010-06-17 13:28:48 -04002925 if (added != null) {
Sunny Goyalc9acdd52015-02-26 12:34:42 -08002926 addAppsToAllApps(context, added);
Sunny Goyal4390ace2014-10-13 11:33:11 -07002927 for (AppInfo ai : added) {
2928 addedOrUpdatedApps.put(ai.componentName, ai);
2929 }
Joe Onorato36115782010-06-17 13:28:48 -04002930 }
Adam Cohen76a47a12014-02-05 11:47:43 -08002931
Joe Onorato36115782010-06-17 13:28:48 -04002932 if (modified != null) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002933 final Callbacks callbacks = getCallback();
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002934 final ArrayList<AppInfo> modifiedFinal = modified;
Sunny Goyal4390ace2014-10-13 11:33:11 -07002935 for (AppInfo ai : modified) {
2936 addedOrUpdatedApps.put(ai.componentName, ai);
Winson Chung64359a52013-07-08 17:17:08 -07002937 }
2938
Joe Onorato36115782010-06-17 13:28:48 -04002939 mHandler.post(new Runnable() {
2940 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -08002941 Callbacks cb = getCallback();
Winson Chungcd2b0142011-06-08 16:02:26 -07002942 if (callbacks == cb && cb != null) {
Joe Onorato36115782010-06-17 13:28:48 -04002943 callbacks.bindAppsUpdated(modifiedFinal);
2944 }
2945 }
2946 });
2947 }
Winson Chung83892cc2013-05-01 16:53:33 -07002948
Sunny Goyal4390ace2014-10-13 11:33:11 -07002949 // Update shortcut infos
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07002950 if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07002951 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
2952 final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
2953 final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
2954
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07002955 synchronized (sBgLock) {
Sunny Goyale2df0622015-04-24 11:27:00 -07002956 for (ItemInfo info : sBgItemsIdMap) {
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07002957 if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
2958 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyal4390ace2014-10-13 11:33:11 -07002959 boolean infoUpdated = false;
2960 boolean shortcutUpdated = false;
2961
2962 // Update shortcuts which use iconResource.
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07002963 if ((si.iconResource != null)
Sunny Goyalda891c12016-03-18 18:29:24 -07002964 && pkgFilter.matches(si.iconResource.packageName)) {
Sunny Goyal53d7ee42015-05-22 12:25:45 -07002965 Bitmap icon = Utilities.createIconBitmap(
2966 si.iconResource.packageName,
2967 si.iconResource.resourceName, context);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07002968 if (icon != null) {
2969 si.setIcon(icon);
2970 si.usingFallbackIcon = false;
Sunny Goyal4390ace2014-10-13 11:33:11 -07002971 infoUpdated = true;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07002972 }
2973 }
Sunny Goyal4390ace2014-10-13 11:33:11 -07002974
2975 ComponentName cn = si.getTargetComponent();
Sunny Goyalda891c12016-03-18 18:29:24 -07002976 if (cn != null && pkgFilter.matches(cn.getPackageName())) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07002977 AppInfo appInfo = addedOrUpdatedApps.get(cn);
2978
2979 if (si.isPromise()) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07002980 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
2981 // Auto install icon
2982 PackageManager pm = context.getPackageManager();
2983 ResolveInfo matched = pm.resolveActivity(
2984 new Intent(Intent.ACTION_MAIN)
2985 .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
2986 PackageManager.MATCH_DEFAULT_ONLY);
2987 if (matched == null) {
2988 // Try to find the best match activity.
2989 Intent intent = pm.getLaunchIntentForPackage(
2990 cn.getPackageName());
2991 if (intent != null) {
2992 cn = intent.getComponent();
2993 appInfo = addedOrUpdatedApps.get(cn);
2994 }
2995
2996 if ((intent == null) || (appInfo == null)) {
2997 removedShortcuts.add(si);
2998 continue;
2999 }
3000 si.promisedIntent = intent;
3001 }
3002 }
3003
3004 // Restore the shortcut.
Sunny Goyalfa401a12015-04-10 13:45:42 -07003005 if (appInfo != null) {
3006 si.flags = appInfo.flags;
3007 }
Sunny Goyal4390ace2014-10-13 11:33:11 -07003008
Sunny Goyal756adbc2015-04-16 15:20:51 -07003009 si.intent = si.promisedIntent;
3010 si.promisedIntent = null;
3011 si.status = ShortcutInfo.DEFAULT;
Sunny Goyal4390ace2014-10-13 11:33:11 -07003012 infoUpdated = true;
3013 si.updateIcon(mIconCache);
3014 }
3015
3016 if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
3017 && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
3018 si.updateIcon(mIconCache);
Winson Chung82b016c2015-05-08 17:00:10 -07003019 si.title = Utilities.trim(appInfo.title);
Sunny Goyal4390ace2014-10-13 11:33:11 -07003020 si.contentDescription = appInfo.contentDescription;
3021 infoUpdated = true;
3022 }
3023
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003024 int oldDisabledFlags = si.isDisabled;
3025 si.isDisabled = flagOp.apply(si.isDisabled);
3026 if (si.isDisabled != oldDisabledFlags) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07003027 shortcutUpdated = true;
3028 }
3029 }
3030
3031 if (infoUpdated || shortcutUpdated) {
3032 updatedShortcuts.add(si);
3033 }
3034 if (infoUpdated) {
3035 updateItemInDatabase(context, si);
3036 }
Sunny Goyalda891c12016-03-18 18:29:24 -07003037 } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07003038 LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
3039 if (mUser.equals(widgetInfo.user)
3040 && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
Sunny Goyalda891c12016-03-18 18:29:24 -07003041 && pkgFilter.matches(widgetInfo.providerName.getPackageName())) {
Sunny Goyal53f96722015-07-13 19:54:53 -07003042 widgetInfo.restoreStatus &=
3043 ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
3044 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
Sunny Goyal84b4adc2015-08-12 15:12:16 -07003045
3046 // adding this flag ensures that launcher shows 'click to setup'
3047 // if the widget has a config activity. In case there is no config
3048 // activity, it will be marked as 'restored' during bind.
3049 widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
3050
Sunny Goyal4390ace2014-10-13 11:33:11 -07003051 widgets.add(widgetInfo);
3052 updateItemInDatabase(context, widgetInfo);
3053 }
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003054 }
3055 }
3056 }
3057
Sunny Goyal4390ace2014-10-13 11:33:11 -07003058 if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003059 final Callbacks callbacks = getCallback();
Sunny Goyal4390ace2014-10-13 11:33:11 -07003060 mHandler.post(new Runnable() {
3061
3062 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -08003063 Callbacks cb = getCallback();
Sunny Goyal4390ace2014-10-13 11:33:11 -07003064 if (callbacks == cb && cb != null) {
3065 callbacks.bindShortcutsChanged(
3066 updatedShortcuts, removedShortcuts, mUser);
3067 }
3068 }
3069 });
3070 if (!removedShortcuts.isEmpty()) {
3071 deleteItemsFromDatabase(context, removedShortcuts);
3072 }
3073 }
3074 if (!widgets.isEmpty()) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003075 final Callbacks callbacks = getCallback();
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003076 mHandler.post(new Runnable() {
3077 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -08003078 Callbacks cb = getCallback();
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003079 if (callbacks == cb && cb != null) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07003080 callbacks.bindWidgetsRestored(widgets);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003081 }
3082 }
3083 });
3084 }
3085 }
3086
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003087 final HashSet<String> removedPackages = new HashSet<>();
3088 final HashSet<ComponentName> removedComponents = new HashSet<>();
3089 if (mOp == OP_REMOVE) {
Winson Chungdf95eb12013-10-16 14:57:07 -07003090 // Mark all packages in the broadcast to be removed
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003091 Collections.addAll(removedPackages, packages);
3092
3093 // No need to update the removedComponents as
3094 // removedPackages is a super-set of removedComponents
Winson Chungdf95eb12013-10-16 14:57:07 -07003095 } else if (mOp == OP_UPDATE) {
3096 // Mark disabled packages in the broadcast to be removed
Winson Chungdf95eb12013-10-16 14:57:07 -07003097 for (int i=0; i<N; i++) {
Kenny Guyed131872014-04-30 03:02:21 +01003098 if (isPackageDisabled(context, packages[i], mUser)) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003099 removedPackages.add(packages[i]);
Winson Chung64359a52013-07-08 17:17:08 -07003100 }
3101 }
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003102
3103 // Update removedComponents as some components can get removed during package update
3104 for (AppInfo info : removedApps) {
3105 removedComponents.add(info.componentName);
3106 }
Winson Chungdf95eb12013-10-16 14:57:07 -07003107 }
Sunny Goyal1a745e82014-10-02 15:58:31 -07003108
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003109 if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
3110 for (String pn : removedPackages) {
3111 deletePackageFromDatabase(context, pn, mUser);
3112 }
3113 for (ComponentName cn : removedComponents) {
3114 deleteItemsFromDatabase(context, getItemInfoForComponentName(cn, mUser));
Sunny Goyal1a745e82014-10-02 15:58:31 -07003115 }
3116
Winson Chungdf95eb12013-10-16 14:57:07 -07003117 // Remove any queued items from the install queue
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003118 InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
3119
Winson Chungdf95eb12013-10-16 14:57:07 -07003120 // Call the components-removed callback
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003121 final Callbacks callbacks = getCallback();
Joe Onorato36115782010-06-17 13:28:48 -04003122 mHandler.post(new Runnable() {
3123 public void run() {
Sunny Goyale0f58d72014-11-10 18:05:31 -08003124 Callbacks cb = getCallback();
Winson Chungcd2b0142011-06-08 16:02:26 -07003125 if (callbacks == cb && cb != null) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003126 callbacks.bindWorkspaceComponentsRemoved(
3127 removedPackages, removedComponents, mUser);
3128 }
3129 }
3130 });
3131 }
3132
3133 if (!removedApps.isEmpty()) {
3134 // Remove corresponding apps from All-Apps
3135 final Callbacks callbacks = getCallback();
3136 mHandler.post(new Runnable() {
3137 public void run() {
3138 Callbacks cb = getCallback();
3139 if (callbacks == cb && cb != null) {
3140 callbacks.bindAppInfosRemoved(removedApps);
Joe Onorato36115782010-06-17 13:28:48 -04003141 }
3142 }
3143 });
Joe Onoratobe386092009-11-17 17:32:16 -08003144 }
Hyunyoung Song227239e2015-05-04 18:17:35 -07003145
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003146 // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
3147 // get widget update signals.
3148 if (!Utilities.ATLEAST_MARSHMALLOW &&
3149 (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003150 final Callbacks callbacks = getCallback();
Hyunyoung Songd4af1482015-04-20 20:40:03 -07003151 mHandler.post(new Runnable() {
Hyunyoung Songd4af1482015-04-20 20:40:03 -07003152 public void run() {
3153 Callbacks cb = getCallback();
3154 if (callbacks == cb && cb != null) {
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003155 callbacks.notifyWidgetProvidersChanged();
Hyunyoung Songd4af1482015-04-20 20:40:03 -07003156 }
3157 }
3158 });
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003159 }
3160 }
3161 }
3162
3163 private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
3164 mHandler.post(new Runnable() {
3165 @Override
3166 public void run() {
3167 Callbacks cb = getCallback();
3168 if (callbacks == cb && cb != null) {
3169 callbacks.bindWidgetsModel(model);
3170 }
Hyunyoung Songd4af1482015-04-20 20:40:03 -07003171 }
3172 });
3173 }
3174
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003175 public void refreshAndBindWidgetsAndShortcuts(
3176 final Callbacks callbacks, final boolean bindFirst) {
3177 runOnWorkerThread(new Runnable() {
3178 @Override
3179 public void run() {
3180 if (bindFirst && !mBgWidgetsModel.isEmpty()) {
3181 bindWidgetsModel(callbacks, mBgWidgetsModel.clone());
Sunny Goyal31860582015-09-18 08:38:57 -07003182 }
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003183 final WidgetsModel model = mBgWidgetsModel.updateAndClone(mApp.getContext());
3184 bindWidgetsModel(callbacks, model);
3185 // update the Widget entries inside DB on the worker thread.
3186 LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
3187 model.getRawList());
Sunny Goyal31860582015-09-18 08:38:57 -07003188 }
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003189 });
Michael Jurkac402cd92013-05-20 15:49:32 +02003190 }
3191
Adam Cohen091440a2015-03-18 14:16:05 -07003192 @Thunk static boolean isPackageDisabled(Context context, String packageName,
Kenny Guyed131872014-04-30 03:02:21 +01003193 UserHandleCompat user) {
3194 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3195 return !launcherApps.isPackageEnabledForProfile(packageName, user);
Winson Chungdf95eb12013-10-16 14:57:07 -07003196 }
Adam Cohen556f6132014-01-15 15:18:08 -08003197
Kenny Guyed131872014-04-30 03:02:21 +01003198 public static boolean isValidPackageActivity(Context context, ComponentName cn,
3199 UserHandleCompat user) {
Winson Chungee055712013-07-30 14:46:24 -07003200 if (cn == null) {
3201 return false;
3202 }
Kenny Guyed131872014-04-30 03:02:21 +01003203 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3204 if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
Winson Chungdf95eb12013-10-16 14:57:07 -07003205 return false;
3206 }
Kenny Guyed131872014-04-30 03:02:21 +01003207 return launcherApps.isActivityEnabledForProfile(cn, user);
Winson Chungee055712013-07-30 14:46:24 -07003208 }
3209
Adam Cohena28b78e2014-05-20 17:03:04 -07003210 public static boolean isValidPackage(Context context, String packageName,
3211 UserHandleCompat user) {
3212 if (packageName == null) {
3213 return false;
3214 }
3215 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
3216 return launcherApps.isPackageEnabledForProfile(packageName, user);
3217 }
3218
Joe Onorato9c1289c2009-08-17 11:03:03 -04003219 /**
Chris Wrenf4d08112014-01-16 18:13:56 -05003220 * Make an ShortcutInfo object for a restored application or shortcut item that points
3221 * to a package that is not yet installed on the system.
3222 */
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003223 public ShortcutInfo getRestoredItemInfo(Cursor c, int titleIndex, Intent intent,
Sunny Goyalc22841b2015-07-13 19:59:50 -07003224 int promiseType, int itemType, CursorIconInfo iconInfo, Context context) {
Chris Wrenf4d08112014-01-16 18:13:56 -05003225 final ShortcutInfo info = new ShortcutInfo();
Kenny Guyed131872014-04-30 03:02:21 +01003226 info.user = UserHandleCompat.myUserHandle();
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003227
3228 Bitmap icon = iconInfo.loadIcon(c, info, context);
3229 // the fallback icon
3230 if (icon == null) {
3231 mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */);
3232 } else {
3233 info.setIcon(icon);
3234 }
Sunny Goyal34942622014-08-29 17:20:55 -07003235
3236 if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003237 String title = (c != null) ? c.getString(titleIndex) : null;
Sunny Goyal34942622014-08-29 17:20:55 -07003238 if (!TextUtils.isEmpty(title)) {
Winson Chung82b016c2015-05-08 17:00:10 -07003239 info.title = Utilities.trim(title);
Sunny Goyal34942622014-08-29 17:20:55 -07003240 }
Sunny Goyal34942622014-08-29 17:20:55 -07003241 } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
3242 if (TextUtils.isEmpty(info.title)) {
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003243 info.title = (c != null) ? Utilities.trim(c.getString(titleIndex)) : "";
Sunny Goyal34942622014-08-29 17:20:55 -07003244 }
Sunny Goyal34942622014-08-29 17:20:55 -07003245 } else {
3246 throw new InvalidParameterException("Invalid restoreType " + promiseType);
3247 }
3248
Winson Chung82b016c2015-05-08 17:00:10 -07003249 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
Sunny Goyalc22841b2015-07-13 19:59:50 -07003250 info.itemType = itemType;
Sunny Goyal34942622014-08-29 17:20:55 -07003251 info.promisedIntent = intent;
Sunny Goyal756adbc2015-04-16 15:20:51 -07003252 info.status = promiseType;
Chris Wrenf4d08112014-01-16 18:13:56 -05003253 return info;
3254 }
3255
3256 /**
3257 * Make an Intent object for a restored application or shortcut item that points
3258 * to the market page for the item.
3259 */
Adam Cohen091440a2015-03-18 14:16:05 -07003260 @Thunk Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
Chris Wrenf4d08112014-01-16 18:13:56 -05003261 ComponentName componentName = intent.getComponent();
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003262 return getMarketIntent(componentName.getPackageName());
3263 }
3264
3265 static Intent getMarketIntent(String packageName) {
3266 return new Intent(Intent.ACTION_VIEW)
3267 .setData(new Uri.Builder()
Chris Wrenf4d08112014-01-16 18:13:56 -05003268 .scheme("market")
3269 .authority("details")
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003270 .appendQueryParameter("id", packageName)
3271 .build());
Chris Wrenf4d08112014-01-16 18:13:56 -05003272 }
3273
3274 /**
Joe Onorato56d82912010-03-07 14:32:10 -05003275 * Make an ShortcutInfo object for a shortcut that is an application.
3276 *
3277 * If c is not null, then it will be used to fill in missing data like the title and icon.
3278 */
Sunny Goyald09c3702016-04-06 16:18:20 -07003279 public ShortcutInfo getAppShortcutInfo(Intent intent,
Kenny Guyed131872014-04-30 03:02:21 +01003280 UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
Sunny Goyal34b65272015-03-11 16:56:52 -07003281 boolean allowMissingTarget, boolean useLowResIcon) {
Kenny Guyed131872014-04-30 03:02:21 +01003282 if (user == null) {
3283 Log.d(TAG, "Null user found in getShortcutInfo");
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003284 return null;
3285 }
3286
Kenny Guyed131872014-04-30 03:02:21 +01003287 ComponentName componentName = intent.getComponent();
3288 if (componentName == null) {
Sunny Goyalb740f592015-12-17 23:22:42 -08003289 Log.d(TAG, "Missing component found in getShortcutInfo");
Kenny Guyed131872014-04-30 03:02:21 +01003290 return null;
3291 }
3292
3293 Intent newIntent = new Intent(intent.getAction(), null);
3294 newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
3295 newIntent.setComponent(componentName);
3296 LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
Sunny Goyalf599ccf2014-07-08 13:01:29 -07003297 if ((lai == null) && !allowMissingTarget) {
Kenny Guyed131872014-04-30 03:02:21 +01003298 Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
3299 return null;
3300 }
3301
3302 final ShortcutInfo info = new ShortcutInfo();
Sunny Goyal34b65272015-03-11 16:56:52 -07003303 mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon);
Sunny Goyal4fbc3822015-02-18 16:46:50 -08003304 if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) {
3305 Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context);
3306 info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
Kenny Guyed131872014-04-30 03:02:21 +01003307 }
3308
Sunny Goyald09c3702016-04-06 16:18:20 -07003309 if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
3310 info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
3311 }
3312
Joe Onorato56d82912010-03-07 14:32:10 -05003313 // from the db
Sunny Goyal4fbc3822015-02-18 16:46:50 -08003314 if (TextUtils.isEmpty(info.title) && c != null) {
Winson Chung82b016c2015-05-08 17:00:10 -07003315 info.title = Utilities.trim(c.getString(titleIndex));
Joe Onorato56d82912010-03-07 14:32:10 -05003316 }
Sunny Goyal4fbc3822015-02-18 16:46:50 -08003317
Joe Onorato56d82912010-03-07 14:32:10 -05003318 // fall back to the class name of the activity
3319 if (info.title == null) {
3320 info.title = componentName.getClassName();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003321 }
Sunny Goyal4fbc3822015-02-18 16:46:50 -08003322
Joe Onorato9c1289c2009-08-17 11:03:03 -04003323 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
Kenny Guyed131872014-04-30 03:02:21 +01003324 info.user = user;
Winson Chung82b016c2015-05-08 17:00:10 -07003325 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
Sunny Goyalfa401a12015-04-10 13:45:42 -07003326 if (lai != null) {
3327 info.flags = AppInfo.initFlags(lai);
3328 }
Joe Onorato9c1289c2009-08-17 11:03:03 -04003329 return info;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003330 }
The Android Open Source Projectbc219c32009-03-09 11:52:14 -07003331
Sunny Goyale2df0622015-04-24 11:27:00 -07003332 static ArrayList<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos,
Winson Chung64359a52013-07-08 17:17:08 -07003333 ItemInfoFilter f) {
3334 HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
3335 for (ItemInfo i : infos) {
3336 if (i instanceof ShortcutInfo) {
3337 ShortcutInfo info = (ShortcutInfo) i;
Sunny Goyal34942622014-08-29 17:20:55 -07003338 ComponentName cn = info.getTargetComponent();
Winson Chung64359a52013-07-08 17:17:08 -07003339 if (cn != null && f.filterItem(null, info, cn)) {
3340 filtered.add(info);
3341 }
3342 } else if (i instanceof FolderInfo) {
3343 FolderInfo info = (FolderInfo) i;
3344 for (ShortcutInfo s : info.contents) {
Sunny Goyal34942622014-08-29 17:20:55 -07003345 ComponentName cn = s.getTargetComponent();
Winson Chung64359a52013-07-08 17:17:08 -07003346 if (cn != null && f.filterItem(info, s, cn)) {
3347 filtered.add(s);
Winson Chung8a435102012-08-30 17:16:53 -07003348 }
3349 }
Winson Chung64359a52013-07-08 17:17:08 -07003350 } else if (i instanceof LauncherAppWidgetInfo) {
3351 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
3352 ComponentName cn = info.providerName;
3353 if (cn != null && f.filterItem(null, info, cn)) {
3354 filtered.add(info);
3355 }
Winson Chung8a435102012-08-30 17:16:53 -07003356 }
3357 }
Winson Chung64359a52013-07-08 17:17:08 -07003358 return new ArrayList<ItemInfo>(filtered);
3359 }
3360
Adam Cohen091440a2015-03-18 14:16:05 -07003361 @Thunk ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
Kenny Guyed131872014-04-30 03:02:21 +01003362 final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07003363 ItemInfoFilter filter = new ItemInfoFilter() {
3364 @Override
3365 public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01003366 if (info.user == null) {
3367 return cn.equals(cname);
3368 } else {
3369 return cn.equals(cname) && info.user.equals(user);
3370 }
Winson Chung64359a52013-07-08 17:17:08 -07003371 }
3372 };
Sunny Goyale2df0622015-04-24 11:27:00 -07003373 return filterItemInfos(sBgItemsIdMap, filter);
Winson Chung64359a52013-07-08 17:17:08 -07003374 }
3375
Sunny Goyal1a745e82014-10-02 15:58:31 -07003376 /**
Joe Onorato0589f0f2010-02-08 13:44:00 -08003377 * Make an ShortcutInfo object for a shortcut that isn't an application.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003378 */
Adam Cohen091440a2015-03-18 14:16:05 -07003379 @Thunk ShortcutInfo getShortcutInfo(Cursor c, Context context,
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003380 int titleIndex, CursorIconInfo iconInfo) {
Michael Jurkac9d95c52011-08-29 14:03:34 -07003381 final ShortcutInfo info = new ShortcutInfo();
Kenny Guyed131872014-04-30 03:02:21 +01003382 // Non-app shortcuts are only supported for current user.
3383 info.user = UserHandleCompat.myUserHandle();
Joe Onorato9c1289c2009-08-17 11:03:03 -04003384 info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003385
Joe Onorato8ddc4fd2010-03-17 09:14:50 -07003386 // TODO: If there's an explicit component and we can't install that, delete it.
3387
Winson Chung82b016c2015-05-08 17:00:10 -07003388 info.title = Utilities.trim(c.getString(titleIndex));
Joe Onorato56d82912010-03-07 14:32:10 -05003389
Sunny Goyal4e5cc642015-06-25 16:37:44 -07003390 Bitmap icon = iconInfo.loadIcon(c, info, context);
3391 // the fallback icon
3392 if (icon == null) {
Kenny Guyed131872014-04-30 03:02:21 +01003393 icon = mIconCache.getDefaultIcon(info.user);
Joe Onorato56d82912010-03-07 14:32:10 -05003394 info.usingFallbackIcon = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003395 }
Joe Onoratod8d22da2010-03-11 17:59:11 -08003396 info.setIcon(icon);
Joe Onorato9c1289c2009-08-17 11:03:03 -04003397 return info;
3398 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003399
Sunny Goyal2350bc92014-10-14 16:42:54 -07003400 ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003401 Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
3402 String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
3403 Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
3404
Adam Cohend9198822011-11-22 16:42:47 -08003405 if (intent == null) {
3406 // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
3407 Log.e(TAG, "Can't construct ShorcutInfo with null intent");
3408 return null;
3409 }
3410
Joe Onorato0589f0f2010-02-08 13:44:00 -08003411 Bitmap icon = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003412 boolean customIcon = false;
3413 ShortcutIconResource iconResource = null;
3414
Sunny Goyal2fce90c2014-10-07 12:01:58 -07003415 if (bitmap instanceof Bitmap) {
3416 icon = Utilities.createIconBitmap((Bitmap) bitmap, context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003417 customIcon = true;
3418 } else {
3419 Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003420 if (extra instanceof ShortcutIconResource) {
3421 iconResource = (ShortcutIconResource) extra;
3422 icon = Utilities.createIconBitmap(iconResource.packageName,
Sunny Goyal53d7ee42015-05-22 12:25:45 -07003423 iconResource.resourceName, context);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003424 }
3425 }
3426
Michael Jurkac9d95c52011-08-29 14:03:34 -07003427 final ShortcutInfo info = new ShortcutInfo();
Joe Onorato56d82912010-03-07 14:32:10 -05003428
Kenny Guyed131872014-04-30 03:02:21 +01003429 // Only support intents for current user for now. Intents sent from other
3430 // users wouldn't get here without intent forwarding anyway.
3431 info.user = UserHandleCompat.myUserHandle();
Joe Onorato56d82912010-03-07 14:32:10 -05003432 if (icon == null) {
Sunny Goyal2350bc92014-10-14 16:42:54 -07003433 icon = mIconCache.getDefaultIcon(info.user);
3434 info.usingFallbackIcon = true;
Joe Onorato56d82912010-03-07 14:32:10 -05003435 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08003436 info.setIcon(icon);
Joe Onorato56d82912010-03-07 14:32:10 -05003437
Winson Chung82b016c2015-05-08 17:00:10 -07003438 info.title = Utilities.trim(name);
3439 info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
Joe Onorato0589f0f2010-02-08 13:44:00 -08003440 info.intent = intent;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003441 info.iconResource = iconResource;
3442
3443 return info;
3444 }
3445
Joe Onorato9c1289c2009-08-17 11:03:03 -04003446 /**
Adam Cohendf2cc412011-04-27 16:56:57 -07003447 * Return an existing FolderInfo object if we have encountered this ID previously,
Joe Onorato9c1289c2009-08-17 11:03:03 -04003448 * or make a new one.
3449 */
Sunny Goyale2df0622015-04-24 11:27:00 -07003450 @Thunk static FolderInfo findOrMakeFolder(LongArrayMap<FolderInfo> folders, long id) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003451 // See if a placeholder was created for us already
3452 FolderInfo folderInfo = folders.get(id);
Adam Cohendf2cc412011-04-27 16:56:57 -07003453 if (folderInfo == null) {
Joe Onorato9c1289c2009-08-17 11:03:03 -04003454 // No placeholder -- create a new instance
Michael Jurkac9d95c52011-08-29 14:03:34 -07003455 folderInfo = new FolderInfo();
Joe Onorato9c1289c2009-08-17 11:03:03 -04003456 folders.put(id, folderInfo);
3457 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003458 return folderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003459 }
3460
Joe Onoratobe386092009-11-17 17:32:16 -08003461
Sunny Goyal651077b2014-06-30 14:15:31 -07003462 static boolean isValidProvider(AppWidgetProviderInfo provider) {
3463 return (provider != null) && (provider.provider != null)
3464 && (provider.provider.getPackageName() != null);
3465 }
3466
Joe Onoratobe386092009-11-17 17:32:16 -08003467 public void dumpState() {
Joe Onoratobe386092009-11-17 17:32:16 -08003468 Log.d(TAG, "mCallbacks=" + mCallbacks);
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003469 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
3470 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
3471 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
3472 AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
Joe Onorato36115782010-06-17 13:28:48 -04003473 if (mLoaderTask != null) {
3474 mLoaderTask.dumpState();
3475 } else {
3476 Log.d(TAG, "mLoaderTask=null");
3477 }
Joe Onoratobe386092009-11-17 17:32:16 -08003478 }
Sunny Goyale0f58d72014-11-10 18:05:31 -08003479
3480 public Callbacks getCallback() {
3481 return mCallbacks != null ? mCallbacks.get() : null;
3482 }
Sunny Goyal18bf8e22015-04-08 18:13:46 -07003483
3484 /**
3485 * @return {@link FolderInfo} if its already loaded.
3486 */
3487 public FolderInfo findFolderById(Long folderId) {
3488 synchronized (sBgLock) {
3489 return sBgFolders.get(folderId);
3490 }
3491 }
Sunny Goyal756adbc2015-04-16 15:20:51 -07003492
Sunny Goyal527c7d32015-08-28 15:19:36 -07003493 @Thunk class DeferredMainThreadExecutor implements Executor {
3494
3495 @Override
3496 public void execute(Runnable command) {
3497 runOnMainThread(command);
3498 }
3499 }
3500
Sunny Goyal756adbc2015-04-16 15:20:51 -07003501 /**
3502 * @return the looper for the worker thread which can be used to start background tasks.
3503 */
3504 public static Looper getWorkerLooper() {
3505 return sWorkerThread.getLooper();
3506 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003507}