Home | History | Annotate | Download | only in launcher3
      1 /*
      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 
     17 package com.android.launcher3;
     18 
     19 import android.app.SearchManager;
     20 import android.appwidget.AppWidgetProviderInfo;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.ContentProviderOperation;
     24 import android.content.ContentResolver;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.Intent.ShortcutIconResource;
     29 import android.content.IntentFilter;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.ProviderInfo;
     32 import android.content.pm.ResolveInfo;
     33 import android.database.Cursor;
     34 import android.graphics.Bitmap;
     35 import android.net.Uri;
     36 import android.os.Build;
     37 import android.os.Environment;
     38 import android.os.Handler;
     39 import android.os.HandlerThread;
     40 import android.os.Looper;
     41 import android.os.Parcelable;
     42 import android.os.Process;
     43 import android.os.SystemClock;
     44 import android.os.TransactionTooLargeException;
     45 import android.provider.BaseColumns;
     46 import android.text.TextUtils;
     47 import android.util.Log;
     48 import android.util.LongSparseArray;
     49 import android.util.Pair;
     50 
     51 import com.android.launcher3.compat.AppWidgetManagerCompat;
     52 import com.android.launcher3.compat.LauncherActivityInfoCompat;
     53 import com.android.launcher3.compat.LauncherAppsCompat;
     54 import com.android.launcher3.compat.PackageInstallerCompat;
     55 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
     56 import com.android.launcher3.compat.UserHandleCompat;
     57 import com.android.launcher3.compat.UserManagerCompat;
     58 import com.android.launcher3.model.WidgetsModel;
     59 import com.android.launcher3.util.ComponentKey;
     60 import com.android.launcher3.util.CursorIconInfo;
     61 import com.android.launcher3.util.LongArrayMap;
     62 import com.android.launcher3.util.ManagedProfileHeuristic;
     63 import com.android.launcher3.util.Thunk;
     64 
     65 import java.lang.ref.WeakReference;
     66 import java.net.URISyntaxException;
     67 import java.security.InvalidParameterException;
     68 import java.util.ArrayList;
     69 import java.util.Arrays;
     70 import java.util.Collection;
     71 import java.util.Collections;
     72 import java.util.Comparator;
     73 import java.util.HashMap;
     74 import java.util.HashSet;
     75 import java.util.Iterator;
     76 import java.util.List;
     77 import java.util.Map.Entry;
     78 import java.util.Set;
     79 
     80 /**
     81  * Maintains in-memory state of the Launcher. It is expected that there should be only one
     82  * LauncherModel object held in a static. Also provide APIs for updating the database state
     83  * for the Launcher.
     84  */
     85 public class LauncherModel extends BroadcastReceiver
     86         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
     87     static final boolean DEBUG_LOADERS = false;
     88     private static final boolean DEBUG_RECEIVER = false;
     89     private static final boolean REMOVE_UNRESTORED_ICONS = true;
     90 
     91     static final String TAG = "Launcher.Model";
     92 
     93     public static final int LOADER_FLAG_NONE = 0;
     94     public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
     95     public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
     96 
     97     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
     98     private static final long INVALID_SCREEN_ID = -1L;
     99 
    100     @Thunk final boolean mAppsCanBeOnRemoveableStorage;
    101     private final boolean mOldContentProviderExists;
    102 
    103     @Thunk final LauncherAppState mApp;
    104     @Thunk final Object mLock = new Object();
    105     @Thunk DeferredHandler mHandler = new DeferredHandler();
    106     @Thunk LoaderTask mLoaderTask;
    107     @Thunk boolean mIsLoaderTaskRunning;
    108     @Thunk boolean mHasLoaderCompletedOnce;
    109 
    110     private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
    111 
    112     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
    113     static {
    114         sWorkerThread.start();
    115     }
    116     @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
    117 
    118     // We start off with everything not loaded.  After that, we assume that
    119     // our monitoring of the package manager provides all updates and we never
    120     // need to do a requery.  These are only ever touched from the loader thread.
    121     @Thunk boolean mWorkspaceLoaded;
    122     @Thunk boolean mAllAppsLoaded;
    123 
    124     // When we are loading pages synchronously, we can't just post the binding of items on the side
    125     // pages as this delays the rotation process.  Instead, we wait for a callback from the first
    126     // draw (in Workspace) to initiate the binding of the remaining side pages.  Any time we start
    127     // a normal load, we also clear this set of Runnables.
    128     static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
    129 
    130     /**
    131      * Set of runnables to be called on the background thread after the workspace binding
    132      * is complete.
    133      */
    134     static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>();
    135 
    136     @Thunk WeakReference<Callbacks> mCallbacks;
    137 
    138     // < only access in worker thread >
    139     AllAppsList mBgAllAppsList;
    140     // Entire list of widgets.
    141     WidgetsModel mBgWidgetsModel;
    142 
    143     // The lock that must be acquired before referencing any static bg data structures.  Unlike
    144     // other locks, this one can generally be held long-term because we never expect any of these
    145     // static data structures to be referenced outside of the worker thread except on the first
    146     // load after configuration change.
    147     static final Object sBgLock = new Object();
    148 
    149     // sBgItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
    150     // LauncherModel to their ids
    151     static final LongArrayMap<ItemInfo> sBgItemsIdMap = new LongArrayMap<>();
    152 
    153     // sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts
    154     //       created by LauncherModel that are directly on the home screen (however, no widgets or
    155     //       shortcuts within folders).
    156     static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>();
    157 
    158     // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
    159     static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets =
    160         new ArrayList<LauncherAppWidgetInfo>();
    161 
    162     // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
    163     static final LongArrayMap<FolderInfo> sBgFolders = new LongArrayMap<>();
    164 
    165     // sBgWorkspaceScreens is the ordered set of workspace screens.
    166     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
    167 
    168     // sBgWidgetProviders is the set of widget providers including custom internal widgets
    169     public static HashMap<ComponentKey, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
    170 
    171     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
    172     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
    173             new HashMap<UserHandleCompat, HashSet<String>>();
    174 
    175     // </ only access in worker thread >
    176 
    177     @Thunk IconCache mIconCache;
    178 
    179     @Thunk final LauncherAppsCompat mLauncherApps;
    180     @Thunk final UserManagerCompat mUserManager;
    181 
    182     public interface Callbacks {
    183         public boolean setLoadOnResume();
    184         public int getCurrentWorkspaceScreen();
    185         public void startBinding();
    186         public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
    187                               boolean forceAnimateIcons);
    188         public void bindScreens(ArrayList<Long> orderedScreenIds);
    189         public void bindAddScreens(ArrayList<Long> orderedScreenIds);
    190         public void bindFolders(LongArrayMap<FolderInfo> folders);
    191         public void finishBindingItems();
    192         public void bindAppWidget(LauncherAppWidgetInfo info);
    193         public void bindAllApplications(ArrayList<AppInfo> apps);
    194         public void bindAppsAdded(ArrayList<Long> newScreens,
    195                                   ArrayList<ItemInfo> addNotAnimated,
    196                                   ArrayList<ItemInfo> addAnimated,
    197                                   ArrayList<AppInfo> addedApps);
    198         public void bindAppsUpdated(ArrayList<AppInfo> apps);
    199         public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
    200                 ArrayList<ShortcutInfo> removed, UserHandleCompat user);
    201         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
    202         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
    203         public void bindComponentsRemoved(ArrayList<String> packageNames,
    204                         ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
    205         public void bindAllPackages(WidgetsModel model);
    206         public void bindSearchablesChanged();
    207         public boolean isAllAppsButtonRank(int rank);
    208         public void onPageBoundSynchronously(int page);
    209         public void dumpLogsToLocalData();
    210     }
    211 
    212     public interface ItemInfoFilter {
    213         public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
    214     }
    215 
    216     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
    217         Context context = app.getContext();
    218 
    219         mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
    220         String oldProvider = context.getString(R.string.old_launcher_provider_uri);
    221         // 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);
    228 
    229         Log.d(TAG, "Old launcher provider: " + oldProvider);
    230         mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
    231 
    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 
    238         mApp = app;
    239         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
    240         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
    241         mIconCache = iconCache;
    242 
    243         mLauncherApps = LauncherAppsCompat.getInstance(context);
    244         mUserManager = UserManagerCompat.getInstance(context);
    245     }
    246 
    247     /** Runs the specified runnable immediately if called from the main thread, otherwise it is
    248      * posted on the main thread handler. */
    249     @Thunk void runOnMainThread(Runnable r) {
    250         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. */
    260     private static void runOnWorkerThread(Runnable r) {
    261         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 
    269     /**
    270      * Runs the specified runnable after the loader is complete
    271      */
    272     @Thunk void runAfterBindCompletes(Runnable r) {
    273         if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) {
    274             synchronized (mBindCompleteRunnables) {
    275                 mBindCompleteRunnables.add(r);
    276             }
    277         } else {
    278             runOnWorkerThread(r);
    279         }
    280     }
    281 
    282     boolean canMigrateFromOldLauncherDb(Launcher launcher) {
    283         return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
    284     }
    285 
    286     public void setPackageState(final PackageInstallInfo installInfo) {
    287         Runnable updateRunnable = new Runnable() {
    288 
    289             @Override
    290             public void run() {
    291                 synchronized (sBgLock) {
    292                     final HashSet<ItemInfo> updates = new HashSet<>();
    293 
    294                     if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
    295                         // Ignore install success events as they are handled by Package add events.
    296                         return;
    297                     }
    298 
    299                     for (ItemInfo info : sBgItemsIdMap) {
    300                         if (info instanceof ShortcutInfo) {
    301                             ShortcutInfo si = (ShortcutInfo) info;
    302                             ComponentName cn = si.getTargetComponent();
    303                             if (si.isPromise() && (cn != null)
    304                                     && installInfo.packageName.equals(cn.getPackageName())) {
    305                                 si.setInstallProgress(installInfo.progress);
    306 
    307                                 if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
    308                                     // Mark this info as broken.
    309                                     si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
    310                                 }
    311                                 updates.add(si);
    312                             }
    313                         }
    314                     }
    315 
    316                     for (LauncherAppWidgetInfo widget : sBgAppWidgets) {
    317                         if (widget.providerName.getPackageName().equals(installInfo.packageName)) {
    318                             widget.installProgress = installInfo.progress;
    319                             updates.add(widget);
    320                         }
    321                     }
    322 
    323                     if (!updates.isEmpty()) {
    324                         // Push changes to the callback.
    325                         Runnable r = new Runnable() {
    326                             public void run() {
    327                                 Callbacks callbacks = getCallback();
    328                                 if (callbacks != null) {
    329                                     callbacks.bindRestoreItemsChange(updates);
    330                                 }
    331                             }
    332                         };
    333                         mHandler.post(r);
    334                     }
    335                 }
    336             }
    337         };
    338         runOnWorkerThread(updateRunnable);
    339     }
    340 
    341     /**
    342      * Updates the icons and label of all pending icons for the provided package name.
    343      */
    344     public void updateSessionDisplayInfo(final String packageName) {
    345         Runnable updateRunnable = new Runnable() {
    346 
    347             @Override
    348             public void run() {
    349                 synchronized (sBgLock) {
    350                     final ArrayList<ShortcutInfo> updates = new ArrayList<>();
    351                     final UserHandleCompat user = UserHandleCompat.myUserHandle();
    352 
    353                     for (ItemInfo info : sBgItemsIdMap) {
    354                         if (info instanceof ShortcutInfo) {
    355                             ShortcutInfo si = (ShortcutInfo) info;
    356                             ComponentName cn = si.getTargetComponent();
    357                             if (si.isPromise() && (cn != null)
    358                                     && packageName.equals(cn.getPackageName())) {
    359                                 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
    360                                     // For auto install apps update the icon as well as label.
    361                                     mIconCache.getTitleAndIcon(si,
    362                                             si.promisedIntent, user,
    363                                             si.shouldUseLowResIcon());
    364                                 } else {
    365                                     // Only update the icon for restored apps.
    366                                     si.updateIcon(mIconCache);
    367                                 }
    368                                 updates.add(si);
    369                             }
    370                         }
    371                     }
    372 
    373                     if (!updates.isEmpty()) {
    374                         // Push changes to the callback.
    375                         Runnable r = new Runnable() {
    376                             public void run() {
    377                                 Callbacks callbacks = getCallback();
    378                                 if (callbacks != null) {
    379                                     callbacks.bindShortcutsChanged(updates,
    380                                             new ArrayList<ShortcutInfo>(), user);
    381                                 }
    382                             }
    383                         };
    384                         mHandler.post(r);
    385                     }
    386                 }
    387             }
    388         };
    389         runOnWorkerThread(updateRunnable);
    390     }
    391 
    392     public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
    393         final Callbacks callbacks = getCallback();
    394 
    395         if (allAppsApps == null) {
    396             throw new RuntimeException("allAppsApps must not be null");
    397         }
    398         if (allAppsApps.isEmpty()) {
    399             return;
    400         }
    401 
    402         // Process the newly added applications and add them to the database first
    403         Runnable r = new Runnable() {
    404             public void run() {
    405                 runOnMainThread(new Runnable() {
    406                     public void run() {
    407                         Callbacks cb = getCallback();
    408                         if (callbacks == cb && cb != null) {
    409                             callbacks.bindAppsAdded(null, null, null, allAppsApps);
    410                         }
    411                     }
    412                 });
    413             }
    414         };
    415         runOnWorkerThread(r);
    416     }
    417 
    418     private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
    419             int[] xy, int spanX, int spanY) {
    420         LauncherAppState app = LauncherAppState.getInstance();
    421         InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
    422         final int xCount = (int) profile.numColumns;
    423         final int yCount = (int) profile.numRows;
    424         boolean[][] occupied = new boolean[xCount][yCount];
    425         if (occupiedPos != null) {
    426             for (ItemInfo r : occupiedPos) {
    427                 int right = r.cellX + r.spanX;
    428                 int bottom = r.cellY + r.spanY;
    429                 for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
    430                     for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
    431                         occupied[x][y] = true;
    432                     }
    433                 }
    434             }
    435         }
    436         return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
    437     }
    438 
    439     /**
    440      * Find a position on the screen for the given size or adds a new screen.
    441      * @return screenId and the coordinates for the item.
    442      */
    443     @Thunk Pair<Long, int[]> findSpaceForItem(
    444             Context context,
    445             ArrayList<Long> workspaceScreens,
    446             ArrayList<Long> addedWorkspaceScreensFinal,
    447             int spanX, int spanY) {
    448         LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
    449 
    450         // Use sBgItemsIdMap as all the items are already loaded.
    451         assertWorkspaceLoaded();
    452         synchronized (sBgLock) {
    453             for (ItemInfo info : sBgItemsIdMap) {
    454                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
    455                     ArrayList<ItemInfo> items = screenItems.get(info.screenId);
    456                     if (items == null) {
    457                         items = new ArrayList<>();
    458                         screenItems.put(info.screenId, items);
    459                     }
    460                     items.add(info);
    461                 }
    462             }
    463         }
    464 
    465         // Find appropriate space for the item.
    466         long screenId = 0;
    467         int[] cordinates = new int[2];
    468         boolean found = false;
    469 
    470         int screenCount = workspaceScreens.size();
    471         // First check the preferred screen.
    472         int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
    473         if (preferredScreenIndex < screenCount) {
    474             screenId = workspaceScreens.get(preferredScreenIndex);
    475             found = findNextAvailableIconSpaceInScreen(
    476                     screenItems.get(screenId), cordinates, spanX, spanY);
    477         }
    478 
    479         if (!found) {
    480             // Search on any of the screens starting from the first screen.
    481             for (int screen = 1; screen < screenCount; screen++) {
    482                 screenId = workspaceScreens.get(screen);
    483                 if (findNextAvailableIconSpaceInScreen(
    484                         screenItems.get(screenId), cordinates, spanX, spanY)) {
    485                     // We found a space for it
    486                     found = true;
    487                     break;
    488                 }
    489             }
    490         }
    491 
    492         if (!found) {
    493             // Still no position found. Add a new screen to the end.
    494             screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
    495 
    496             // Save the screen id for binding in the workspace
    497             workspaceScreens.add(screenId);
    498             addedWorkspaceScreensFinal.add(screenId);
    499 
    500             // If we still can't find an empty space, then God help us all!!!
    501             if (!findNextAvailableIconSpaceInScreen(
    502                     screenItems.get(screenId), cordinates, spanX, spanY)) {
    503                 throw new RuntimeException("Can't find space to add the item");
    504             }
    505         }
    506         return Pair.create(screenId, cordinates);
    507     }
    508 
    509     /**
    510      * Adds the provided items to the workspace.
    511      */
    512     public void addAndBindAddedWorkspaceItems(final Context context,
    513             final ArrayList<? extends ItemInfo> workspaceApps) {
    514         final Callbacks callbacks = getCallback();
    515         if (workspaceApps.isEmpty()) {
    516             return;
    517         }
    518         // Process the newly added applications and add them to the database first
    519         Runnable r = new Runnable() {
    520             public void run() {
    521                 final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>();
    522                 final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
    523 
    524                 // Get the list of workspace screens.  We need to append to this list and
    525                 // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
    526                 // called.
    527                 ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context);
    528                 synchronized(sBgLock) {
    529                     for (ItemInfo item : workspaceApps) {
    530                         if (item instanceof ShortcutInfo) {
    531                             // Short-circuit this logic if the icon exists somewhere on the workspace
    532                             if (shortcutExists(context, item.getIntent(), item.user)) {
    533                                 continue;
    534                             }
    535                         }
    536 
    537                         // Find appropriate space for the item.
    538                         Pair<Long, int[]> coords = findSpaceForItem(context,
    539                                 workspaceScreens, addedWorkspaceScreensFinal,
    540                                 1, 1);
    541                         long screenId = coords.first;
    542                         int[] cordinates = coords.second;
    543 
    544                         ItemInfo itemInfo;
    545                         if (item instanceof ShortcutInfo || item instanceof FolderInfo) {
    546                             itemInfo = item;
    547                         } else if (item instanceof AppInfo) {
    548                             itemInfo = ((AppInfo) item).makeShortcut();
    549                         } else {
    550                             throw new RuntimeException("Unexpected info type");
    551                         }
    552 
    553                         // Add the shortcut to the db
    554                         addItemToDatabase(context, itemInfo,
    555                                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
    556                                 screenId, cordinates[0], cordinates[1]);
    557                         // Save the ShortcutInfo for binding in the workspace
    558                         addedShortcutsFinal.add(itemInfo);
    559                     }
    560                 }
    561 
    562                 // Update the workspace screens
    563                 updateWorkspaceScreenOrder(context, workspaceScreens);
    564 
    565                 if (!addedShortcutsFinal.isEmpty()) {
    566                     runOnMainThread(new Runnable() {
    567                         public void run() {
    568                             Callbacks cb = getCallback();
    569                             if (callbacks == cb && cb != null) {
    570                                 final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
    571                                 final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
    572                                 if (!addedShortcutsFinal.isEmpty()) {
    573                                     ItemInfo info = addedShortcutsFinal.get(addedShortcutsFinal.size() - 1);
    574                                     long lastScreenId = info.screenId;
    575                                     for (ItemInfo i : addedShortcutsFinal) {
    576                                         if (i.screenId == lastScreenId) {
    577                                             addAnimated.add(i);
    578                                         } else {
    579                                             addNotAnimated.add(i);
    580                                         }
    581                                     }
    582                                 }
    583                                 callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
    584                                         addNotAnimated, addAnimated, null);
    585                             }
    586                         }
    587                     });
    588                 }
    589             }
    590         };
    591         runOnWorkerThread(r);
    592     }
    593 
    594     private void unbindItemInfosAndClearQueuedBindRunnables() {
    595         if (sWorkerThread.getThreadId() == Process.myTid()) {
    596             throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
    597                     "main thread");
    598         }
    599 
    600         // Clear any deferred bind runnables
    601         synchronized (mDeferredBindRunnables) {
    602             mDeferredBindRunnables.clear();
    603         }
    604 
    605         // Remove any queued UI runnables
    606         mHandler.cancelAll();
    607         // Unbind all the workspace items
    608         unbindWorkspaceItemsOnMainThread();
    609     }
    610 
    611     /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
    612     void unbindWorkspaceItemsOnMainThread() {
    613         // Ensure that we don't use the same workspace items data structure on the main thread
    614         // by making a copy of workspace items first.
    615         final ArrayList<ItemInfo> tmpItems = new ArrayList<ItemInfo>();
    616         synchronized (sBgLock) {
    617             tmpItems.addAll(sBgWorkspaceItems);
    618             tmpItems.addAll(sBgAppWidgets);
    619         }
    620         Runnable r = new Runnable() {
    621                 @Override
    622                 public void run() {
    623                    for (ItemInfo item : tmpItems) {
    624                        item.unbind();
    625                    }
    626                 }
    627             };
    628         runOnMainThread(r);
    629     }
    630 
    631     /**
    632      * Adds an item to the DB if it was not created previously, or move it to a new
    633      * <container, screen, cellX, cellY>
    634      */
    635     static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
    636             long screenId, int cellX, int cellY) {
    637         if (item.container == ItemInfo.NO_ID) {
    638             // From all apps
    639             addItemToDatabase(context, item, container, screenId, cellX, cellY);
    640         } else {
    641             // From somewhere else
    642             moveItemInDatabase(context, item, container, screenId, cellX, cellY);
    643         }
    644     }
    645 
    646     static void checkItemInfoLocked(
    647             final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
    648         ItemInfo modelItem = sBgItemsIdMap.get(itemId);
    649         if (modelItem != null && item != modelItem) {
    650             // check all the data is consistent
    651             if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
    652                 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
    653                 ShortcutInfo shortcut = (ShortcutInfo) item;
    654                 if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
    655                         modelShortcut.intent.filterEquals(shortcut.intent) &&
    656                         modelShortcut.id == shortcut.id &&
    657                         modelShortcut.itemType == shortcut.itemType &&
    658                         modelShortcut.container == shortcut.container &&
    659                         modelShortcut.screenId == shortcut.screenId &&
    660                         modelShortcut.cellX == shortcut.cellX &&
    661                         modelShortcut.cellY == shortcut.cellY &&
    662                         modelShortcut.spanX == shortcut.spanX &&
    663                         modelShortcut.spanY == shortcut.spanY &&
    664                         ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
    665                         (modelShortcut.dropPos != null &&
    666                                 shortcut.dropPos != null &&
    667                                 modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
    668                         modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
    669                     // For all intents and purposes, this is the same object
    670                     return;
    671                 }
    672             }
    673 
    674             // the modelItem needs to match up perfectly with item if our model is
    675             // to be consistent with the database-- for now, just require
    676             // modelItem == item or the equality check above
    677             String msg = "item: " + ((item != null) ? item.toString() : "null") +
    678                     "modelItem: " +
    679                     ((modelItem != null) ? modelItem.toString() : "null") +
    680                     "Error: ItemInfo passed to checkItemInfo doesn't match original";
    681             RuntimeException e = new RuntimeException(msg);
    682             if (stackTrace != null) {
    683                 e.setStackTrace(stackTrace);
    684             }
    685             throw e;
    686         }
    687     }
    688 
    689     static void checkItemInfo(final ItemInfo item) {
    690         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    691         final long itemId = item.id;
    692         Runnable r = new Runnable() {
    693             public void run() {
    694                 synchronized (sBgLock) {
    695                     checkItemInfoLocked(itemId, item, stackTrace);
    696                 }
    697             }
    698         };
    699         runOnWorkerThread(r);
    700     }
    701 
    702     static void updateItemInDatabaseHelper(Context context, final ContentValues values,
    703             final ItemInfo item, final String callingFunction) {
    704         final long itemId = item.id;
    705         final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
    706         final ContentResolver cr = context.getContentResolver();
    707 
    708         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    709         Runnable r = new Runnable() {
    710             public void run() {
    711                 cr.update(uri, values, null, null);
    712                 updateItemArrays(item, itemId, stackTrace);
    713             }
    714         };
    715         runOnWorkerThread(r);
    716     }
    717 
    718     static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
    719             final ArrayList<ItemInfo> items, final String callingFunction) {
    720         final ContentResolver cr = context.getContentResolver();
    721 
    722         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
    723         Runnable r = new Runnable() {
    724             public void run() {
    725                 ArrayList<ContentProviderOperation> ops =
    726                         new ArrayList<ContentProviderOperation>();
    727                 int count = items.size();
    728                 for (int i = 0; i < count; i++) {
    729                     ItemInfo item = items.get(i);
    730                     final long itemId = item.id;
    731                     final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
    732                     ContentValues values = valuesList.get(i);
    733 
    734                     ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
    735                     updateItemArrays(item, itemId, stackTrace);
    736 
    737                 }
    738                 try {
    739                     cr.applyBatch(LauncherProvider.AUTHORITY, ops);
    740                 } catch (Exception e) {
    741                     e.printStackTrace();
    742                 }
    743             }
    744         };
    745         runOnWorkerThread(r);
    746     }
    747 
    748     static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
    749         // Lock on mBgLock *after* the db operation
    750         synchronized (sBgLock) {
    751             checkItemInfoLocked(itemId, item, stackTrace);
    752 
    753             if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
    754                     item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    755                 // Item is in a folder, make sure this folder exists
    756                 if (!sBgFolders.containsKey(item.container)) {
    757                     // An items container is being set to a that of an item which is not in
    758                     // the list of Folders.
    759                     String msg = "item: " + item + " container being set to: " +
    760                             item.container + ", not in the list of folders";
    761                     Log.e(TAG, msg);
    762                 }
    763             }
    764 
    765             // Items are added/removed from the corresponding FolderInfo elsewhere, such
    766             // as in Workspace.onDrop. Here, we just add/remove them from the list of items
    767             // that are on the desktop, as appropriate
    768             ItemInfo modelItem = sBgItemsIdMap.get(itemId);
    769             if (modelItem != null &&
    770                     (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
    771                      modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
    772                 switch (modelItem.itemType) {
    773                     case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
    774                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
    775                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
    776                         if (!sBgWorkspaceItems.contains(modelItem)) {
    777                             sBgWorkspaceItems.add(modelItem);
    778                         }
    779                         break;
    780                     default:
    781                         break;
    782                 }
    783             } else {
    784                 sBgWorkspaceItems.remove(modelItem);
    785             }
    786         }
    787     }
    788 
    789     /**
    790      * Move an item in the DB to a new <container, screen, cellX, cellY>
    791      */
    792     public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
    793             final long screenId, final int cellX, final int cellY) {
    794         item.container = container;
    795         item.cellX = cellX;
    796         item.cellY = cellY;
    797 
    798         // We store hotseat items in canonical form which is this orientation invariant position
    799         // in the hotseat
    800         if (context instanceof Launcher && screenId < 0 &&
    801                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    802             item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
    803         } else {
    804             item.screenId = screenId;
    805         }
    806 
    807         final ContentValues values = new ContentValues();
    808         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
    809         values.put(LauncherSettings.Favorites.CELLX, item.cellX);
    810         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
    811         values.put(LauncherSettings.Favorites.RANK, item.rank);
    812         values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
    813 
    814         updateItemInDatabaseHelper(context, values, item, "moveItemInDatabase");
    815     }
    816 
    817     /**
    818      * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
    819      * cellX, cellY have already been updated on the ItemInfos.
    820      */
    821     static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
    822             final long container, final int screen) {
    823 
    824         ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
    825         int count = items.size();
    826 
    827         for (int i = 0; i < count; i++) {
    828             ItemInfo item = items.get(i);
    829             item.container = container;
    830 
    831             // We store hotseat items in canonical form which is this orientation invariant position
    832             // in the hotseat
    833             if (context instanceof Launcher && screen < 0 &&
    834                     container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    835                 item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
    836                         item.cellY);
    837             } else {
    838                 item.screenId = screen;
    839             }
    840 
    841             final ContentValues values = new ContentValues();
    842             values.put(LauncherSettings.Favorites.CONTAINER, item.container);
    843             values.put(LauncherSettings.Favorites.CELLX, item.cellX);
    844             values.put(LauncherSettings.Favorites.CELLY, item.cellY);
    845             values.put(LauncherSettings.Favorites.RANK, item.rank);
    846             values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
    847 
    848             contentValues.add(values);
    849         }
    850         updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
    851     }
    852 
    853     /**
    854      * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
    855      */
    856     static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
    857             final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
    858         item.container = container;
    859         item.cellX = cellX;
    860         item.cellY = cellY;
    861         item.spanX = spanX;
    862         item.spanY = spanY;
    863 
    864         // We store hotseat items in canonical form which is this orientation invariant position
    865         // in the hotseat
    866         if (context instanceof Launcher && screenId < 0 &&
    867                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    868             item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
    869         } else {
    870             item.screenId = screenId;
    871         }
    872 
    873         final ContentValues values = new ContentValues();
    874         values.put(LauncherSettings.Favorites.CONTAINER, item.container);
    875         values.put(LauncherSettings.Favorites.CELLX, item.cellX);
    876         values.put(LauncherSettings.Favorites.CELLY, item.cellY);
    877         values.put(LauncherSettings.Favorites.RANK, item.rank);
    878         values.put(LauncherSettings.Favorites.SPANX, item.spanX);
    879         values.put(LauncherSettings.Favorites.SPANY, item.spanY);
    880         values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
    881 
    882         updateItemInDatabaseHelper(context, values, item, "modifyItemInDatabase");
    883     }
    884 
    885     /**
    886      * Update an item to the database in a specified container.
    887      */
    888     public static void updateItemInDatabase(Context context, final ItemInfo item) {
    889         final ContentValues values = new ContentValues();
    890         item.onAddToDatabase(context, values);
    891         updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
    892     }
    893 
    894     private void assertWorkspaceLoaded() {
    895         if (LauncherAppState.isDogfoodBuild() && (isLoadingWorkspace() || !mHasLoaderCompletedOnce)) {
    896             throw new RuntimeException("Trying to add shortcut while loader is running");
    897         }
    898     }
    899 
    900     /**
    901      * Returns true if the shortcuts already exists on the workspace. This must be called after
    902      * the workspace has been loaded. We identify a shortcut by its intent.
    903      */
    904     @Thunk boolean shortcutExists(Context context, Intent intent, UserHandleCompat user) {
    905         assertWorkspaceLoaded();
    906         final String intentWithPkg, intentWithoutPkg;
    907         if (intent.getComponent() != null) {
    908             // If component is not null, an intent with null package will produce
    909             // the same result and should also be a match.
    910             String packageName = intent.getComponent().getPackageName();
    911             if (intent.getPackage() != null) {
    912                 intentWithPkg = intent.toUri(0);
    913                 intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
    914             } else {
    915                 intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
    916                 intentWithoutPkg = intent.toUri(0);
    917             }
    918         } else {
    919             intentWithPkg = intent.toUri(0);
    920             intentWithoutPkg = intent.toUri(0);
    921         }
    922 
    923         synchronized (sBgLock) {
    924             for (ItemInfo item : sBgItemsIdMap) {
    925                 if (item instanceof ShortcutInfo) {
    926                     ShortcutInfo info = (ShortcutInfo) item;
    927                     Intent targetIntent = info.promisedIntent == null
    928                             ? info.intent : info.promisedIntent;
    929                     if (targetIntent != null && info.user.equals(user)) {
    930                         String s = targetIntent.toUri(0);
    931                         if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
    932                             return true;
    933                         }
    934                     }
    935                 }
    936             }
    937         }
    938         return false;
    939     }
    940 
    941     /**
    942      * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
    943      */
    944     FolderInfo getFolderById(Context context, LongArrayMap<FolderInfo> folderList, long id) {
    945         final ContentResolver cr = context.getContentResolver();
    946         Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
    947                 "_id=? and (itemType=? or itemType=?)",
    948                 new String[] { String.valueOf(id),
    949                         String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
    950 
    951         try {
    952             if (c.moveToFirst()) {
    953                 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
    954                 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
    955                 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
    956                 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
    957                 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
    958                 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
    959                 final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
    960 
    961                 FolderInfo folderInfo = null;
    962                 switch (c.getInt(itemTypeIndex)) {
    963                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
    964                         folderInfo = findOrMakeFolder(folderList, id);
    965                         break;
    966                 }
    967 
    968                 // Do not trim the folder label, as is was set by the user.
    969                 folderInfo.title = c.getString(titleIndex);
    970                 folderInfo.id = id;
    971                 folderInfo.container = c.getInt(containerIndex);
    972                 folderInfo.screenId = c.getInt(screenIndex);
    973                 folderInfo.cellX = c.getInt(cellXIndex);
    974                 folderInfo.cellY = c.getInt(cellYIndex);
    975                 folderInfo.options = c.getInt(optionsIndex);
    976 
    977                 return folderInfo;
    978             }
    979         } finally {
    980             c.close();
    981         }
    982 
    983         return null;
    984     }
    985 
    986     /**
    987      * Add an item to the database in a specified container. Sets the container, screen, cellX and
    988      * cellY fields of the item. Also assigns an ID to the item.
    989      */
    990     public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
    991             final long screenId, final int cellX, final int cellY) {
    992         item.container = container;
    993         item.cellX = cellX;
    994         item.cellY = cellY;
    995         // We store hotseat items in canonical form which is this orientation invariant position
    996         // in the hotseat
    997         if (context instanceof Launcher && screenId < 0 &&
    998                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
    999             item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
   1000         } else {
   1001             item.screenId = screenId;
   1002         }
   1003 
   1004         final ContentValues values = new ContentValues();
   1005         final ContentResolver cr = context.getContentResolver();
   1006         item.onAddToDatabase(context, values);
   1007 
   1008         item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
   1009         values.put(LauncherSettings.Favorites._ID, item.id);
   1010 
   1011         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
   1012         Runnable r = new Runnable() {
   1013             public void run() {
   1014                 cr.insert(LauncherSettings.Favorites.CONTENT_URI, values);
   1015 
   1016                 // Lock on mBgLock *after* the db operation
   1017                 synchronized (sBgLock) {
   1018                     checkItemInfoLocked(item.id, item, stackTrace);
   1019                     sBgItemsIdMap.put(item.id, item);
   1020                     switch (item.itemType) {
   1021                         case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   1022                             sBgFolders.put(item.id, (FolderInfo) item);
   1023                             // Fall through
   1024                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   1025                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   1026                             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
   1027                                     item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   1028                                 sBgWorkspaceItems.add(item);
   1029                             } else {
   1030                                 if (!sBgFolders.containsKey(item.container)) {
   1031                                     // Adding an item to a folder that doesn't exist.
   1032                                     String msg = "adding item: " + item + " to a folder that " +
   1033                                             " doesn't exist";
   1034                                     Log.e(TAG, msg);
   1035                                 }
   1036                             }
   1037                             break;
   1038                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
   1039                             sBgAppWidgets.add((LauncherAppWidgetInfo) item);
   1040                             break;
   1041                     }
   1042                 }
   1043             }
   1044         };
   1045         runOnWorkerThread(r);
   1046     }
   1047 
   1048     /**
   1049      * Creates a new unique child id, for a given cell span across all layouts.
   1050      */
   1051     static int getCellLayoutChildId(
   1052             long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
   1053         return (((int) container & 0xFF) << 24)
   1054                 | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
   1055     }
   1056 
   1057     private static ArrayList<ItemInfo> getItemsByPackageName(
   1058             final String pn, final UserHandleCompat user) {
   1059         ItemInfoFilter filter  = new ItemInfoFilter() {
   1060             @Override
   1061             public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
   1062                 return cn.getPackageName().equals(pn) && info.user.equals(user);
   1063             }
   1064         };
   1065         return filterItemInfos(sBgItemsIdMap, filter);
   1066     }
   1067 
   1068     /**
   1069      * Removes all the items from the database corresponding to the specified package.
   1070      */
   1071     static void deletePackageFromDatabase(Context context, final String pn,
   1072             final UserHandleCompat user) {
   1073         deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
   1074     }
   1075 
   1076     /**
   1077      * Removes the specified item from the database
   1078      * @param context
   1079      * @param item
   1080      */
   1081     public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
   1082         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
   1083         items.add(item);
   1084         deleteItemsFromDatabase(context, items);
   1085     }
   1086 
   1087     /**
   1088      * Removes the specified items from the database
   1089      * @param context
   1090      * @param item
   1091      */
   1092     static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
   1093         final ContentResolver cr = context.getContentResolver();
   1094         Runnable r = new Runnable() {
   1095             public void run() {
   1096                 for (ItemInfo item : items) {
   1097                     final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
   1098                     cr.delete(uri, null, null);
   1099 
   1100                     // Lock on mBgLock *after* the db operation
   1101                     synchronized (sBgLock) {
   1102                         switch (item.itemType) {
   1103                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   1104                                 sBgFolders.remove(item.id);
   1105                                 for (ItemInfo info: sBgItemsIdMap) {
   1106                                     if (info.container == item.id) {
   1107                                         // We are deleting a folder which still contains items that
   1108                                         // think they are contained by that folder.
   1109                                         String msg = "deleting a folder (" + item + ") which still " +
   1110                                                 "contains items (" + info + ")";
   1111                                         Log.e(TAG, msg);
   1112                                     }
   1113                                 }
   1114                                 sBgWorkspaceItems.remove(item);
   1115                                 break;
   1116                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   1117                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   1118                                 sBgWorkspaceItems.remove(item);
   1119                                 break;
   1120                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
   1121                                 sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
   1122                                 break;
   1123                         }
   1124                         sBgItemsIdMap.remove(item.id);
   1125                     }
   1126                 }
   1127             }
   1128         };
   1129         runOnWorkerThread(r);
   1130     }
   1131 
   1132     /**
   1133      * Update the order of the workspace screens in the database. The array list contains
   1134      * a list of screen ids in the order that they should appear.
   1135      */
   1136     void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
   1137         final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
   1138         final ContentResolver cr = context.getContentResolver();
   1139         final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
   1140 
   1141         // Remove any negative screen ids -- these aren't persisted
   1142         Iterator<Long> iter = screensCopy.iterator();
   1143         while (iter.hasNext()) {
   1144             long id = iter.next();
   1145             if (id < 0) {
   1146                 iter.remove();
   1147             }
   1148         }
   1149 
   1150         Runnable r = new Runnable() {
   1151             @Override
   1152             public void run() {
   1153                 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
   1154                 // Clear the table
   1155                 ops.add(ContentProviderOperation.newDelete(uri).build());
   1156                 int count = screensCopy.size();
   1157                 for (int i = 0; i < count; i++) {
   1158                     ContentValues v = new ContentValues();
   1159                     long screenId = screensCopy.get(i);
   1160                     v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
   1161                     v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
   1162                     ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
   1163                 }
   1164 
   1165                 try {
   1166                     cr.applyBatch(LauncherProvider.AUTHORITY, ops);
   1167                 } catch (Exception ex) {
   1168                     throw new RuntimeException(ex);
   1169                 }
   1170 
   1171                 synchronized (sBgLock) {
   1172                     sBgWorkspaceScreens.clear();
   1173                     sBgWorkspaceScreens.addAll(screensCopy);
   1174                 }
   1175             }
   1176         };
   1177         runOnWorkerThread(r);
   1178     }
   1179 
   1180     /**
   1181      * Remove the contents of the specified folder from the database
   1182      */
   1183     public static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
   1184         final ContentResolver cr = context.getContentResolver();
   1185 
   1186         Runnable r = new Runnable() {
   1187             public void run() {
   1188                 cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
   1189                 // Lock on mBgLock *after* the db operation
   1190                 synchronized (sBgLock) {
   1191                     sBgItemsIdMap.remove(info.id);
   1192                     sBgFolders.remove(info.id);
   1193                     sBgWorkspaceItems.remove(info);
   1194                 }
   1195 
   1196                 cr.delete(LauncherSettings.Favorites.CONTENT_URI,
   1197                         LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
   1198                 // Lock on mBgLock *after* the db operation
   1199                 synchronized (sBgLock) {
   1200                     for (ItemInfo childInfo : info.contents) {
   1201                         sBgItemsIdMap.remove(childInfo.id);
   1202                     }
   1203                 }
   1204             }
   1205         };
   1206         runOnWorkerThread(r);
   1207     }
   1208 
   1209     /**
   1210      * Set this as the current Launcher activity object for the loader.
   1211      */
   1212     public void initialize(Callbacks callbacks) {
   1213         synchronized (mLock) {
   1214             // Disconnect any of the callbacks and drawables associated with ItemInfos on the
   1215             // workspace to prevent leaking Launcher activities on orientation change.
   1216             unbindItemInfosAndClearQueuedBindRunnables();
   1217             mCallbacks = new WeakReference<Callbacks>(callbacks);
   1218         }
   1219     }
   1220 
   1221     @Override
   1222     public void onPackageChanged(String packageName, UserHandleCompat user) {
   1223         int op = PackageUpdatedTask.OP_UPDATE;
   1224         enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
   1225                 user));
   1226     }
   1227 
   1228     @Override
   1229     public void onPackageRemoved(String packageName, UserHandleCompat user) {
   1230         int op = PackageUpdatedTask.OP_REMOVE;
   1231         enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
   1232                 user));
   1233     }
   1234 
   1235     @Override
   1236     public void onPackageAdded(String packageName, UserHandleCompat user) {
   1237         int op = PackageUpdatedTask.OP_ADD;
   1238         enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
   1239                 user));
   1240     }
   1241 
   1242     @Override
   1243     public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
   1244             boolean replacing) {
   1245         if (!replacing) {
   1246             enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
   1247                     user));
   1248             if (mAppsCanBeOnRemoveableStorage) {
   1249                 // Only rebind if we support removable storage. It catches the
   1250                 // case where
   1251                 // apps on the external sd card need to be reloaded
   1252                 startLoaderFromBackground();
   1253             }
   1254         } else {
   1255             // If we are replacing then just update the packages in the list
   1256             enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
   1257                     packageNames, user));
   1258         }
   1259     }
   1260 
   1261     @Override
   1262     public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
   1263             boolean replacing) {
   1264         if (!replacing) {
   1265             enqueuePackageUpdated(new PackageUpdatedTask(
   1266                     PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
   1267                     user));
   1268         }
   1269     }
   1270 
   1271     /**
   1272      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
   1273      * ACTION_PACKAGE_CHANGED.
   1274      */
   1275     @Override
   1276     public void onReceive(Context context, Intent intent) {
   1277         if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
   1278 
   1279         final String action = intent.getAction();
   1280         if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
   1281             // If we have changed locale we need to clear out the labels in all apps/workspace.
   1282             forceReload();
   1283         } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
   1284                    SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
   1285             Callbacks callbacks = getCallback();
   1286             if (callbacks != null) {
   1287                 callbacks.bindSearchablesChanged();
   1288             }
   1289         } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
   1290                 || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
   1291             forceReload();
   1292         }
   1293     }
   1294 
   1295     void forceReload() {
   1296         resetLoadedState(true, true);
   1297 
   1298         // Do this here because if the launcher activity is running it will be restarted.
   1299         // If it's not running startLoaderFromBackground will merely tell it that it needs
   1300         // to reload.
   1301         startLoaderFromBackground();
   1302     }
   1303 
   1304     public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
   1305         synchronized (mLock) {
   1306             // Stop any existing loaders first, so they don't set mAllAppsLoaded or
   1307             // mWorkspaceLoaded to true later
   1308             stopLoaderLocked();
   1309             if (resetAllAppsLoaded) mAllAppsLoaded = false;
   1310             if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
   1311         }
   1312     }
   1313 
   1314     /**
   1315      * When the launcher is in the background, it's possible for it to miss paired
   1316      * configuration changes.  So whenever we trigger the loader from the background
   1317      * tell the launcher that it needs to re-run the loader when it comes back instead
   1318      * of doing it now.
   1319      */
   1320     public void startLoaderFromBackground() {
   1321         boolean runLoader = false;
   1322         Callbacks callbacks = getCallback();
   1323         if (callbacks != null) {
   1324             // Only actually run the loader if they're not paused.
   1325             if (!callbacks.setLoadOnResume()) {
   1326                 runLoader = true;
   1327             }
   1328         }
   1329         if (runLoader) {
   1330             startLoader(PagedView.INVALID_RESTORE_PAGE);
   1331         }
   1332     }
   1333 
   1334     /**
   1335      * If there is already a loader task running, tell it to stop.
   1336      */
   1337     private void stopLoaderLocked() {
   1338         LoaderTask oldTask = mLoaderTask;
   1339         if (oldTask != null) {
   1340             oldTask.stopLocked();
   1341         }
   1342     }
   1343 
   1344     public boolean isCurrentCallbacks(Callbacks callbacks) {
   1345         return (mCallbacks != null && mCallbacks.get() == callbacks);
   1346     }
   1347 
   1348     public void startLoader(int synchronousBindPage) {
   1349         startLoader(synchronousBindPage, LOADER_FLAG_NONE);
   1350     }
   1351 
   1352     public void startLoader(int synchronousBindPage, int loadFlags) {
   1353         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
   1354         InstallShortcutReceiver.enableInstallQueue();
   1355         synchronized (mLock) {
   1356             // Clear any deferred bind-runnables from the synchronized load process
   1357             // We must do this before any loading/binding is scheduled below.
   1358             synchronized (mDeferredBindRunnables) {
   1359                 mDeferredBindRunnables.clear();
   1360             }
   1361 
   1362             // Don't bother to start the thread if we know it's not going to do anything
   1363             if (mCallbacks != null && mCallbacks.get() != null) {
   1364                 // If there is already one running, tell it to stop.
   1365                 stopLoaderLocked();
   1366                 mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
   1367                 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
   1368                         && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
   1369                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
   1370                 } else {
   1371                     sWorkerThread.setPriority(Thread.NORM_PRIORITY);
   1372                     sWorker.post(mLoaderTask);
   1373                 }
   1374             }
   1375         }
   1376     }
   1377 
   1378     void bindRemainingSynchronousPages() {
   1379         // Post the remaining side pages to be loaded
   1380         if (!mDeferredBindRunnables.isEmpty()) {
   1381             Runnable[] deferredBindRunnables = null;
   1382             synchronized (mDeferredBindRunnables) {
   1383                 deferredBindRunnables = mDeferredBindRunnables.toArray(
   1384                         new Runnable[mDeferredBindRunnables.size()]);
   1385                 mDeferredBindRunnables.clear();
   1386             }
   1387             for (final Runnable r : deferredBindRunnables) {
   1388                 mHandler.post(r);
   1389             }
   1390         }
   1391 
   1392         // Run all the bind complete runnables after workspace is bound.
   1393         if (!mBindCompleteRunnables.isEmpty()) {
   1394             synchronized (mBindCompleteRunnables) {
   1395                 for (final Runnable r : mBindCompleteRunnables) {
   1396                     runOnWorkerThread(r);
   1397                 }
   1398                 mBindCompleteRunnables.clear();
   1399             }
   1400         }
   1401     }
   1402 
   1403     public void stopLoader() {
   1404         synchronized (mLock) {
   1405             if (mLoaderTask != null) {
   1406                 mLoaderTask.stopLocked();
   1407             }
   1408         }
   1409     }
   1410 
   1411     /**
   1412      * Loads the workspace screen ids in an ordered list.
   1413      */
   1414     @Thunk static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
   1415         final ContentResolver contentResolver = context.getContentResolver();
   1416         final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
   1417 
   1418         // Get screens ordered by rank.
   1419         final Cursor sc = contentResolver.query(screensUri, null, null, null,
   1420                 LauncherSettings.WorkspaceScreens.SCREEN_RANK);
   1421         ArrayList<Long> screenIds = new ArrayList<Long>();
   1422         try {
   1423             final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID);
   1424             while (sc.moveToNext()) {
   1425                 try {
   1426                     screenIds.add(sc.getLong(idIndex));
   1427                 } catch (Exception e) {
   1428                     Launcher.addDumpLog(TAG, "Desktop items loading interrupted"
   1429                             + " - invalid screens: " + e, true);
   1430                 }
   1431             }
   1432         } finally {
   1433             sc.close();
   1434         }
   1435         return screenIds;
   1436     }
   1437 
   1438     public boolean isAllAppsLoaded() {
   1439         return mAllAppsLoaded;
   1440     }
   1441 
   1442     boolean isLoadingWorkspace() {
   1443         synchronized (mLock) {
   1444             if (mLoaderTask != null) {
   1445                 return mLoaderTask.isLoadingWorkspace();
   1446             }
   1447         }
   1448         return false;
   1449     }
   1450 
   1451     /**
   1452      * Runnable for the thread that loads the contents of the launcher:
   1453      *   - workspace icons
   1454      *   - widgets
   1455      *   - all apps icons
   1456      */
   1457     private class LoaderTask implements Runnable {
   1458         private Context mContext;
   1459         @Thunk boolean mIsLoadingAndBindingWorkspace;
   1460         private boolean mStopped;
   1461         @Thunk boolean mLoadAndBindStepFinished;
   1462         private int mFlags;
   1463 
   1464         LoaderTask(Context context, int flags) {
   1465             mContext = context;
   1466             mFlags = flags;
   1467         }
   1468 
   1469         boolean isLoadingWorkspace() {
   1470             return mIsLoadingAndBindingWorkspace;
   1471         }
   1472 
   1473         private void loadAndBindWorkspace() {
   1474             mIsLoadingAndBindingWorkspace = true;
   1475 
   1476             // Load the workspace
   1477             if (DEBUG_LOADERS) {
   1478                 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
   1479             }
   1480 
   1481             if (!mWorkspaceLoaded) {
   1482                 loadWorkspace();
   1483                 synchronized (LoaderTask.this) {
   1484                     if (mStopped) {
   1485                         return;
   1486                     }
   1487                     mWorkspaceLoaded = true;
   1488                 }
   1489             }
   1490 
   1491             // Bind the workspace
   1492             bindWorkspace(-1);
   1493         }
   1494 
   1495         private void waitForIdle() {
   1496             // Wait until the either we're stopped or the other threads are done.
   1497             // This way we don't start loading all apps until the workspace has settled
   1498             // down.
   1499             synchronized (LoaderTask.this) {
   1500                 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   1501 
   1502                 mHandler.postIdle(new Runnable() {
   1503                         public void run() {
   1504                             synchronized (LoaderTask.this) {
   1505                                 mLoadAndBindStepFinished = true;
   1506                                 if (DEBUG_LOADERS) {
   1507                                     Log.d(TAG, "done with previous binding step");
   1508                                 }
   1509                                 LoaderTask.this.notify();
   1510                             }
   1511                         }
   1512                     });
   1513 
   1514                 while (!mStopped && !mLoadAndBindStepFinished) {
   1515                     try {
   1516                         // Just in case mFlushingWorkerThread changes but we aren't woken up,
   1517                         // wait no longer than 1sec at a time
   1518                         this.wait(1000);
   1519                     } catch (InterruptedException ex) {
   1520                         // Ignore
   1521                     }
   1522                 }
   1523                 if (DEBUG_LOADERS) {
   1524                     Log.d(TAG, "waited "
   1525                             + (SystemClock.uptimeMillis()-workspaceWaitTime)
   1526                             + "ms for previous step to finish binding");
   1527                 }
   1528             }
   1529         }
   1530 
   1531         void runBindSynchronousPage(int synchronousBindPage) {
   1532             if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
   1533                 // Ensure that we have a valid page index to load synchronously
   1534                 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
   1535                         "valid page index");
   1536             }
   1537             if (!mAllAppsLoaded || !mWorkspaceLoaded) {
   1538                 // Ensure that we don't try and bind a specified page when the pages have not been
   1539                 // loaded already (we should load everything asynchronously in that case)
   1540                 throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
   1541             }
   1542             synchronized (mLock) {
   1543                 if (mIsLoaderTaskRunning) {
   1544                     // Ensure that we are never running the background loading at this point since
   1545                     // we also touch the background collections
   1546                     throw new RuntimeException("Error! Background loading is already running");
   1547                 }
   1548             }
   1549 
   1550             // XXX: Throw an exception if we are already loading (since we touch the worker thread
   1551             //      data structures, we can't allow any other thread to touch that data, but because
   1552             //      this call is synchronous, we can get away with not locking).
   1553 
   1554             // The LauncherModel is static in the LauncherAppState and mHandler may have queued
   1555             // operations from the previous activity.  We need to ensure that all queued operations
   1556             // are executed before any synchronous binding work is done.
   1557             mHandler.flush();
   1558 
   1559             // Divide the set of loaded items into those that we are binding synchronously, and
   1560             // everything else that is to be bound normally (asynchronously).
   1561             bindWorkspace(synchronousBindPage);
   1562             // XXX: For now, continue posting the binding of AllApps as there are other issues that
   1563             //      arise from that.
   1564             onlyBindAllApps();
   1565         }
   1566 
   1567         public void run() {
   1568             synchronized (mLock) {
   1569                 if (mStopped) {
   1570                     return;
   1571                 }
   1572                 mIsLoaderTaskRunning = true;
   1573             }
   1574             // Optimize for end-user experience: if the Launcher is up and // running with the
   1575             // All Apps interface in the foreground, load All Apps first. Otherwise, load the
   1576             // workspace first (default).
   1577             keep_running: {
   1578                 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
   1579                 loadAndBindWorkspace();
   1580 
   1581                 if (mStopped) {
   1582                     break keep_running;
   1583                 }
   1584 
   1585                 waitForIdle();
   1586 
   1587                 // second step
   1588                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
   1589                 loadAndBindAllApps();
   1590             }
   1591 
   1592             // Clear out this reference, otherwise we end up holding it until all of the
   1593             // callback runnables are done.
   1594             mContext = null;
   1595 
   1596             synchronized (mLock) {
   1597                 // If we are still the last one to be scheduled, remove ourselves.
   1598                 if (mLoaderTask == this) {
   1599                     mLoaderTask = null;
   1600                 }
   1601                 mIsLoaderTaskRunning = false;
   1602                 mHasLoaderCompletedOnce = true;
   1603             }
   1604         }
   1605 
   1606         public void stopLocked() {
   1607             synchronized (LoaderTask.this) {
   1608                 mStopped = true;
   1609                 this.notify();
   1610             }
   1611         }
   1612 
   1613         /**
   1614          * Gets the callbacks object.  If we've been stopped, or if the launcher object
   1615          * has somehow been garbage collected, return null instead.  Pass in the Callbacks
   1616          * object that was around when the deferred message was scheduled, and if there's
   1617          * a new Callbacks object around then also return null.  This will save us from
   1618          * calling onto it with data that will be ignored.
   1619          */
   1620         Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
   1621             synchronized (mLock) {
   1622                 if (mStopped) {
   1623                     return null;
   1624                 }
   1625 
   1626                 if (mCallbacks == null) {
   1627                     return null;
   1628                 }
   1629 
   1630                 final Callbacks callbacks = mCallbacks.get();
   1631                 if (callbacks != oldCallbacks) {
   1632                     return null;
   1633                 }
   1634                 if (callbacks == null) {
   1635                     Log.w(TAG, "no mCallbacks");
   1636                     return null;
   1637                 }
   1638 
   1639                 return callbacks;
   1640             }
   1641         }
   1642 
   1643         // check & update map of what's occupied; used to discard overlapping/invalid items
   1644         private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item) {
   1645             LauncherAppState app = LauncherAppState.getInstance();
   1646             InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
   1647             final int countX = (int) profile.numColumns;
   1648             final int countY = (int) profile.numRows;
   1649 
   1650             long containerIndex = item.screenId;
   1651             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   1652                 // Return early if we detect that an item is under the hotseat button
   1653                 if (mCallbacks == null ||
   1654                         mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
   1655                     Log.e(TAG, "Error loading shortcut into hotseat " + item
   1656                             + " into position (" + item.screenId + ":" + item.cellX + ","
   1657                             + item.cellY + ") occupied by all apps");
   1658                     return false;
   1659                 }
   1660 
   1661                 final ItemInfo[][] hotseatItems =
   1662                         occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
   1663 
   1664                 if (item.screenId >= profile.numHotseatIcons) {
   1665                     Log.e(TAG, "Error loading shortcut " + item
   1666                             + " into hotseat position " + item.screenId
   1667                             + ", position out of bounds: (0 to " + (profile.numHotseatIcons - 1)
   1668                             + ")");
   1669                     return false;
   1670                 }
   1671 
   1672                 if (hotseatItems != null) {
   1673                     if (hotseatItems[(int) item.screenId][0] != null) {
   1674                         Log.e(TAG, "Error loading shortcut into hotseat " + item
   1675                                 + " into position (" + item.screenId + ":" + item.cellX + ","
   1676                                 + item.cellY + ") occupied by "
   1677                                 + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
   1678                                 [(int) item.screenId][0]);
   1679                             return false;
   1680                     } else {
   1681                         hotseatItems[(int) item.screenId][0] = item;
   1682                         return true;
   1683                     }
   1684                 } else {
   1685                     final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
   1686                     items[(int) item.screenId][0] = item;
   1687                     occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
   1688                     return true;
   1689                 }
   1690             } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   1691                 // Skip further checking if it is not the hotseat or workspace container
   1692                 return true;
   1693             }
   1694 
   1695             if (!occupied.containsKey(item.screenId)) {
   1696                 ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
   1697                 occupied.put(item.screenId, items);
   1698             }
   1699 
   1700             final ItemInfo[][] screens = occupied.get(item.screenId);
   1701             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   1702                     item.cellX < 0 || item.cellY < 0 ||
   1703                     item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
   1704                 Log.e(TAG, "Error loading shortcut " + item
   1705                         + " into cell (" + containerIndex + "-" + item.screenId + ":"
   1706                         + item.cellX + "," + item.cellY
   1707                         + ") out of screen bounds ( " + countX + "x" + countY + ")");
   1708                 return false;
   1709             }
   1710 
   1711             // Check if any workspace icons overlap with each other
   1712             for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
   1713                 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
   1714                     if (screens[x][y] != null) {
   1715                         Log.e(TAG, "Error loading shortcut " + item
   1716                             + " into cell (" + containerIndex + "-" + item.screenId + ":"
   1717                             + x + "," + y
   1718                             + ") occupied by "
   1719                             + screens[x][y]);
   1720                         return false;
   1721                     }
   1722                 }
   1723             }
   1724             for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
   1725                 for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
   1726                     screens[x][y] = item;
   1727                 }
   1728             }
   1729 
   1730             return true;
   1731         }
   1732 
   1733         /** Clears all the sBg data structures */
   1734         private void clearSBgDataStructures() {
   1735             synchronized (sBgLock) {
   1736                 sBgWorkspaceItems.clear();
   1737                 sBgAppWidgets.clear();
   1738                 sBgFolders.clear();
   1739                 sBgItemsIdMap.clear();
   1740                 sBgWorkspaceScreens.clear();
   1741             }
   1742         }
   1743 
   1744         private void loadWorkspace() {
   1745             final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   1746 
   1747             final Context context = mContext;
   1748             final ContentResolver contentResolver = context.getContentResolver();
   1749             final PackageManager manager = context.getPackageManager();
   1750             final boolean isSafeMode = manager.isSafeMode();
   1751             final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
   1752             final boolean isSdCardReady = context.registerReceiver(null,
   1753                     new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
   1754 
   1755             LauncherAppState app = LauncherAppState.getInstance();
   1756             InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
   1757             int countX = (int) profile.numColumns;
   1758             int countY = (int) profile.numRows;
   1759 
   1760             if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
   1761                 Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
   1762                 LauncherAppState.getLauncherProvider().deleteDatabase();
   1763             }
   1764 
   1765             if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
   1766                 // append the user's Launcher2 shortcuts
   1767                 Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
   1768                 LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
   1769             } else {
   1770                 // Make sure the default workspace is loaded
   1771                 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
   1772                 LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
   1773             }
   1774 
   1775             synchronized (sBgLock) {
   1776                 clearSBgDataStructures();
   1777                 final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
   1778                         .getInstance(mContext).updateAndGetActiveSessionCache();
   1779 
   1780                 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
   1781                 final ArrayList<Long> restoredRows = new ArrayList<Long>();
   1782                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
   1783                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
   1784                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
   1785 
   1786                 // +1 for the hotseat (it can be larger than the workspace)
   1787                 // Load workspace in reverse order to ensure that latest items are loaded first (and
   1788                 // before any earlier duplicates)
   1789                 final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
   1790 
   1791                 try {
   1792                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
   1793                     final int intentIndex = c.getColumnIndexOrThrow
   1794                             (LauncherSettings.Favorites.INTENT);
   1795                     final int titleIndex = c.getColumnIndexOrThrow
   1796                             (LauncherSettings.Favorites.TITLE);
   1797                     final int containerIndex = c.getColumnIndexOrThrow(
   1798                             LauncherSettings.Favorites.CONTAINER);
   1799                     final int itemTypeIndex = c.getColumnIndexOrThrow(
   1800                             LauncherSettings.Favorites.ITEM_TYPE);
   1801                     final int appWidgetIdIndex = c.getColumnIndexOrThrow(
   1802                             LauncherSettings.Favorites.APPWIDGET_ID);
   1803                     final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
   1804                             LauncherSettings.Favorites.APPWIDGET_PROVIDER);
   1805                     final int screenIndex = c.getColumnIndexOrThrow(
   1806                             LauncherSettings.Favorites.SCREEN);
   1807                     final int cellXIndex = c.getColumnIndexOrThrow
   1808                             (LauncherSettings.Favorites.CELLX);
   1809                     final int cellYIndex = c.getColumnIndexOrThrow
   1810                             (LauncherSettings.Favorites.CELLY);
   1811                     final int spanXIndex = c.getColumnIndexOrThrow
   1812                             (LauncherSettings.Favorites.SPANX);
   1813                     final int spanYIndex = c.getColumnIndexOrThrow(
   1814                             LauncherSettings.Favorites.SPANY);
   1815                     final int rankIndex = c.getColumnIndexOrThrow(
   1816                             LauncherSettings.Favorites.RANK);
   1817                     final int restoredIndex = c.getColumnIndexOrThrow(
   1818                             LauncherSettings.Favorites.RESTORED);
   1819                     final int profileIdIndex = c.getColumnIndexOrThrow(
   1820                             LauncherSettings.Favorites.PROFILE_ID);
   1821                     final int optionsIndex = c.getColumnIndexOrThrow(
   1822                             LauncherSettings.Favorites.OPTIONS);
   1823                     final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
   1824 
   1825                     final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
   1826                     for (UserHandleCompat user : mUserManager.getUserProfiles()) {
   1827                         allUsers.put(mUserManager.getSerialNumberForUser(user), user);
   1828                     }
   1829 
   1830                     ShortcutInfo info;
   1831                     String intentDescription;
   1832                     LauncherAppWidgetInfo appWidgetInfo;
   1833                     int container;
   1834                     long id;
   1835                     long serialNumber;
   1836                     Intent intent;
   1837                     UserHandleCompat user;
   1838 
   1839                     while (!mStopped && c.moveToNext()) {
   1840                         try {
   1841                             int itemType = c.getInt(itemTypeIndex);
   1842                             boolean restored = 0 != c.getInt(restoredIndex);
   1843                             boolean allowMissingTarget = false;
   1844                             container = c.getInt(containerIndex);
   1845 
   1846                             switch (itemType) {
   1847                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   1848                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   1849                                 id = c.getLong(idIndex);
   1850                                 intentDescription = c.getString(intentIndex);
   1851                                 serialNumber = c.getInt(profileIdIndex);
   1852                                 user = allUsers.get(serialNumber);
   1853                                 int promiseType = c.getInt(restoredIndex);
   1854                                 int disabledState = 0;
   1855                                 boolean itemReplaced = false;
   1856                                 if (user == null) {
   1857                                     // User has been deleted remove the item.
   1858                                     itemsToRemove.add(id);
   1859                                     continue;
   1860                                 }
   1861                                 try {
   1862                                     intent = Intent.parseUri(intentDescription, 0);
   1863                                     ComponentName cn = intent.getComponent();
   1864                                     if (cn != null && cn.getPackageName() != null) {
   1865                                         boolean validPkg = launcherApps.isPackageEnabledForProfile(
   1866                                                 cn.getPackageName(), user);
   1867                                         boolean validComponent = validPkg &&
   1868                                                 launcherApps.isActivityEnabledForProfile(cn, user);
   1869 
   1870                                         if (validComponent) {
   1871                                             if (restored) {
   1872                                                 // no special handling necessary for this item
   1873                                                 restoredRows.add(id);
   1874                                                 restored = false;
   1875                                             }
   1876                                         } else if (validPkg) {
   1877                                             intent = null;
   1878                                             if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
   1879                                                 // We allow auto install apps to have their intent
   1880                                                 // updated after an install.
   1881                                                 intent = manager.getLaunchIntentForPackage(
   1882                                                         cn.getPackageName());
   1883                                                 if (intent != null) {
   1884                                                     ContentValues values = new ContentValues();
   1885                                                     values.put(LauncherSettings.Favorites.INTENT,
   1886                                                             intent.toUri(0));
   1887                                                     updateItem(id, values);
   1888                                                 }
   1889                                             }
   1890 
   1891                                             if (intent == null) {
   1892                                                 // The app is installed but the component is no
   1893                                                 // longer available.
   1894                                                 Launcher.addDumpLog(TAG,
   1895                                                         "Invalid component removed: " + cn, true);
   1896                                                 itemsToRemove.add(id);
   1897                                                 continue;
   1898                                             } else {
   1899                                                 // no special handling necessary for this item
   1900                                                 restoredRows.add(id);
   1901                                                 restored = false;
   1902                                             }
   1903                                         } else if (restored) {
   1904                                             // Package is not yet available but might be
   1905                                             // installed later.
   1906                                             Launcher.addDumpLog(TAG,
   1907                                                     "package not yet restored: " + cn, true);
   1908 
   1909                                             if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
   1910                                                 // Restore has started once.
   1911                                             } else if (installingPkgs.containsKey(cn.getPackageName())) {
   1912                                                 // App restore has started. Update the flag
   1913                                                 promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
   1914                                                 ContentValues values = new ContentValues();
   1915                                                 values.put(LauncherSettings.Favorites.RESTORED,
   1916                                                         promiseType);
   1917                                                 updateItem(id, values);
   1918                                             } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) {
   1919                                                 // This is a common app. Try to replace this.
   1920                                                 int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType);
   1921                                                 CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context);
   1922                                                 if (parser.findDefaultApp()) {
   1923                                                     // Default app found. Replace it.
   1924                                                     intent = parser.parsedIntent;
   1925                                                     cn = intent.getComponent();
   1926                                                     ContentValues values = parser.parsedValues;
   1927                                                     values.put(LauncherSettings.Favorites.RESTORED, 0);
   1928                                                     updateItem(id, values);
   1929                                                     restored = false;
   1930                                                     itemReplaced = true;
   1931 
   1932                                                 } else if (REMOVE_UNRESTORED_ICONS) {
   1933                                                     Launcher.addDumpLog(TAG,
   1934                                                             "Unrestored package removed: " + cn, true);
   1935                                                     itemsToRemove.add(id);
   1936                                                     continue;
   1937                                                 }
   1938                                             } else if (REMOVE_UNRESTORED_ICONS) {
   1939                                                 Launcher.addDumpLog(TAG,
   1940                                                         "Unrestored package removed: " + cn, true);
   1941                                                 itemsToRemove.add(id);
   1942                                                 continue;
   1943                                             }
   1944                                         } else if (launcherApps.isAppEnabled(
   1945                                                 manager, cn.getPackageName(),
   1946                                                 PackageManager.GET_UNINSTALLED_PACKAGES)) {
   1947                                             // Package is present but not available.
   1948                                             allowMissingTarget = true;
   1949                                             disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
   1950                                         } else if (!isSdCardReady) {
   1951                                             // SdCard is not ready yet. Package might get available,
   1952                                             // once it is ready.
   1953                                             Launcher.addDumpLog(TAG, "Invalid package: " + cn
   1954                                                     + " (check again later)", true);
   1955                                             HashSet<String> pkgs = sPendingPackages.get(user);
   1956                                             if (pkgs == null) {
   1957                                                 pkgs = new HashSet<String>();
   1958                                                 sPendingPackages.put(user, pkgs);
   1959                                             }
   1960                                             pkgs.add(cn.getPackageName());
   1961                                             allowMissingTarget = true;
   1962                                             // Add the icon on the workspace anyway.
   1963 
   1964                                         } else {
   1965                                             // Do not wait for external media load anymore.
   1966                                             // Log the invalid package, and remove it
   1967                                             Launcher.addDumpLog(TAG,
   1968                                                     "Invalid package removed: " + cn, true);
   1969                                             itemsToRemove.add(id);
   1970                                             continue;
   1971                                         }
   1972                                     } else if (cn == null) {
   1973                                         // For shortcuts with no component, keep them as they are
   1974                                         restoredRows.add(id);
   1975                                         restored = false;
   1976                                     }
   1977                                 } catch (URISyntaxException e) {
   1978                                     Launcher.addDumpLog(TAG,
   1979                                             "Invalid uri: " + intentDescription, true);
   1980                                     continue;
   1981                                 }
   1982 
   1983                                 boolean useLowResIcon = container >= 0 &&
   1984                                         c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
   1985 
   1986                                 if (itemReplaced) {
   1987                                     if (user.equals(UserHandleCompat.myUserHandle())) {
   1988                                         info = getAppShortcutInfo(manager, intent, user, context, null,
   1989                                                 cursorIconInfo.iconIndex, titleIndex,
   1990                                                 false, useLowResIcon);
   1991                                     } else {
   1992                                         // Don't replace items for other profiles.
   1993                                         itemsToRemove.add(id);
   1994                                         continue;
   1995                                     }
   1996                                 } else if (restored) {
   1997                                     if (user.equals(UserHandleCompat.myUserHandle())) {
   1998                                         Launcher.addDumpLog(TAG,
   1999                                                 "constructing info for partially restored package",
   2000                                                 true);
   2001                                         info = getRestoredItemInfo(c, titleIndex, intent,
   2002                                                 promiseType, itemType, cursorIconInfo, context);
   2003                                         intent = getRestoredItemIntent(c, context, intent);
   2004                                     } else {
   2005                                         // Don't restore items for other profiles.
   2006                                         itemsToRemove.add(id);
   2007                                         continue;
   2008                                     }
   2009                                 } else if (itemType ==
   2010                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
   2011                                     info = getAppShortcutInfo(manager, intent, user, context, c,
   2012                                             cursorIconInfo.iconIndex, titleIndex,
   2013                                             allowMissingTarget, useLowResIcon);
   2014                                 } else {
   2015                                     info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
   2016 
   2017                                     // App shortcuts that used to be automatically added to Launcher
   2018                                     // didn't always have the correct intent flags set, so do that
   2019                                     // here
   2020                                     if (intent.getAction() != null &&
   2021                                         intent.getCategories() != null &&
   2022                                         intent.getAction().equals(Intent.ACTION_MAIN) &&
   2023                                         intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
   2024                                         intent.addFlags(
   2025                                             Intent.FLAG_ACTIVITY_NEW_TASK |
   2026                                             Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   2027                                     }
   2028                                 }
   2029 
   2030                                 if (info != null) {
   2031                                     info.id = id;
   2032                                     info.intent = intent;
   2033                                     info.container = container;
   2034                                     info.screenId = c.getInt(screenIndex);
   2035                                     info.cellX = c.getInt(cellXIndex);
   2036                                     info.cellY = c.getInt(cellYIndex);
   2037                                     info.rank = c.getInt(rankIndex);
   2038                                     info.spanX = 1;
   2039                                     info.spanY = 1;
   2040                                     info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
   2041                                     if (info.promisedIntent != null) {
   2042                                         info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
   2043                                     }
   2044                                     info.isDisabled = disabledState;
   2045                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
   2046                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
   2047                                     }
   2048 
   2049                                     // check & update map of what's occupied
   2050                                     if (!checkItemPlacement(occupied, info)) {
   2051                                         itemsToRemove.add(id);
   2052                                         break;
   2053                                     }
   2054 
   2055                                     if (restored) {
   2056                                         ComponentName cn = info.getTargetComponent();
   2057                                         if (cn != null) {
   2058                                             Integer progress = installingPkgs.get(cn.getPackageName());
   2059                                             if (progress != null) {
   2060                                                 info.setInstallProgress(progress);
   2061                                             } else {
   2062                                                 info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
   2063                                             }
   2064                                         }
   2065                                     }
   2066 
   2067                                     switch (container) {
   2068                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
   2069                                     case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
   2070                                         sBgWorkspaceItems.add(info);
   2071                                         break;
   2072                                     default:
   2073                                         // Item is in a user folder
   2074                                         FolderInfo folderInfo =
   2075                                                 findOrMakeFolder(sBgFolders, container);
   2076                                         folderInfo.add(info);
   2077                                         break;
   2078                                     }
   2079                                     sBgItemsIdMap.put(info.id, info);
   2080                                 } else {
   2081                                     throw new RuntimeException("Unexpected null ShortcutInfo");
   2082                                 }
   2083                                 break;
   2084 
   2085                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
   2086                                 id = c.getLong(idIndex);
   2087                                 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
   2088 
   2089                                 // Do not trim the folder label, as is was set by the user.
   2090                                 folderInfo.title = c.getString(titleIndex);
   2091                                 folderInfo.id = id;
   2092                                 folderInfo.container = container;
   2093                                 folderInfo.screenId = c.getInt(screenIndex);
   2094                                 folderInfo.cellX = c.getInt(cellXIndex);
   2095                                 folderInfo.cellY = c.getInt(cellYIndex);
   2096                                 folderInfo.spanX = 1;
   2097                                 folderInfo.spanY = 1;
   2098                                 folderInfo.options = c.getInt(optionsIndex);
   2099 
   2100                                 // check & update map of what's occupied
   2101                                 if (!checkItemPlacement(occupied, folderInfo)) {
   2102                                     itemsToRemove.add(id);
   2103                                     break;
   2104                                 }
   2105 
   2106                                 switch (container) {
   2107                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
   2108                                     case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
   2109                                         sBgWorkspaceItems.add(folderInfo);
   2110                                         break;
   2111                                 }
   2112 
   2113                                 if (restored) {
   2114                                     // no special handling required for restored folders
   2115                                     restoredRows.add(id);
   2116                                 }
   2117 
   2118                                 sBgItemsIdMap.put(folderInfo.id, folderInfo);
   2119                                 sBgFolders.put(folderInfo.id, folderInfo);
   2120                                 break;
   2121 
   2122                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
   2123                             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
   2124                                 // Read all Launcher-specific widget details
   2125                                 boolean customWidget = itemType ==
   2126                                     LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
   2127 
   2128                                 int appWidgetId = c.getInt(appWidgetIdIndex);
   2129                                 serialNumber = c.getLong(profileIdIndex);
   2130                                 String savedProvider = c.getString(appWidgetProviderIndex);
   2131                                 id = c.getLong(idIndex);
   2132                                 user = allUsers.get(serialNumber);
   2133                                 if (user == null) {
   2134                                     itemsToRemove.add(id);
   2135                                     continue;
   2136                                 }
   2137 
   2138                                 final ComponentName component =
   2139                                         ComponentName.unflattenFromString(savedProvider);
   2140 
   2141                                 final int restoreStatus = c.getInt(restoredIndex);
   2142                                 final boolean isIdValid = (restoreStatus &
   2143                                         LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
   2144                                 final boolean wasProviderReady = (restoreStatus &
   2145                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
   2146 
   2147                                 final LauncherAppWidgetProviderInfo provider =
   2148                                         LauncherModel.getProviderInfo(context,
   2149                                                 ComponentName.unflattenFromString(savedProvider),
   2150                                                 user);
   2151 
   2152                                 final boolean isProviderReady = isValidProvider(provider);
   2153                                 if (!isSafeMode && !customWidget &&
   2154                                         wasProviderReady && !isProviderReady) {
   2155                                     String log = "Deleting widget that isn't installed anymore: "
   2156                                             + "id=" + id + " appWidgetId=" + appWidgetId;
   2157 
   2158                                     Log.e(TAG, log);
   2159                                     Launcher.addDumpLog(TAG, log, false);
   2160                                     itemsToRemove.add(id);
   2161                                 } else {
   2162                                     if (isProviderReady) {
   2163                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
   2164                                                 provider.provider);
   2165 
   2166                                         int status = restoreStatus;
   2167                                         if (!wasProviderReady) {
   2168                                             // If provider was not previously ready, update the
   2169                                             // status and UI flag.
   2170 
   2171                                             // Id would be valid only if the widget restore broadcast was received.
   2172                                             if (isIdValid) {
   2173                                                 status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
   2174                                             } else {
   2175                                                 status &= ~LauncherAppWidgetInfo
   2176                                                         .FLAG_PROVIDER_NOT_READY;
   2177                                             }
   2178                                         }
   2179                                         appWidgetInfo.restoreStatus = status;
   2180                                     } else {
   2181                                         Log.v(TAG, "Widget restore pending id=" + id
   2182                                                 + " appWidgetId=" + appWidgetId
   2183                                                 + " status =" + restoreStatus);
   2184                                         appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
   2185                                                 component);
   2186                                         appWidgetInfo.restoreStatus = restoreStatus;
   2187                                         Integer installProgress = installingPkgs.get(component.getPackageName());
   2188 
   2189                                         if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
   2190                                             // Restore has started once.
   2191                                         } else if (installProgress != null) {
   2192                                             // App restore has started. Update the flag
   2193                                             appWidgetInfo.restoreStatus |=
   2194                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
   2195                                         } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
   2196                                             Launcher.addDumpLog(TAG,
   2197                                                     "Unrestored widget removed: " + component, true);
   2198                                             itemsToRemove.add(id);
   2199                                             continue;
   2200                                         }
   2201 
   2202                                         appWidgetInfo.installProgress =
   2203                                                 installProgress == null ? 0 : installProgress;
   2204                                     }
   2205 
   2206                                     appWidgetInfo.id = id;
   2207                                     appWidgetInfo.screenId = c.getInt(screenIndex);
   2208                                     appWidgetInfo.cellX = c.getInt(cellXIndex);
   2209                                     appWidgetInfo.cellY = c.getInt(cellYIndex);
   2210                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
   2211                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
   2212                                     appWidgetInfo.user = user;
   2213 
   2214                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   2215                                         container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2216                                         Log.e(TAG, "Widget found where container != " +
   2217                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
   2218                                         continue;
   2219                                     }
   2220 
   2221                                     appWidgetInfo.container = container;
   2222                                     // check & update map of what's occupied
   2223                                     if (!checkItemPlacement(occupied, appWidgetInfo)) {
   2224                                         itemsToRemove.add(id);
   2225                                         break;
   2226                                     }
   2227 
   2228                                     if (!customWidget) {
   2229                                         String providerName =
   2230                                                 appWidgetInfo.providerName.flattenToString();
   2231                                         if (!providerName.equals(savedProvider) ||
   2232                                                 (appWidgetInfo.restoreStatus != restoreStatus)) {
   2233                                             ContentValues values = new ContentValues();
   2234                                             values.put(
   2235                                                     LauncherSettings.Favorites.APPWIDGET_PROVIDER,
   2236                                                     providerName);
   2237                                             values.put(LauncherSettings.Favorites.RESTORED,
   2238                                                     appWidgetInfo.restoreStatus);
   2239                                             updateItem(id, values);
   2240                                         }
   2241                                     }
   2242                                     sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
   2243                                     sBgAppWidgets.add(appWidgetInfo);
   2244                                 }
   2245                                 break;
   2246                             }
   2247                         } catch (Exception e) {
   2248                             Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
   2249                         }
   2250                     }
   2251                 } finally {
   2252                     if (c != null) {
   2253                         c.close();
   2254                     }
   2255                 }
   2256 
   2257                 // Break early if we've stopped loading
   2258                 if (mStopped) {
   2259                     clearSBgDataStructures();
   2260                     return;
   2261                 }
   2262 
   2263                 if (itemsToRemove.size() > 0) {
   2264                     // Remove dead items
   2265                     contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
   2266                             Utilities.createDbSelectionQuery(
   2267                                     LauncherSettings.Favorites._ID, itemsToRemove), null);
   2268                     if (DEBUG_LOADERS) {
   2269                         Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
   2270                                 LauncherSettings.Favorites._ID, itemsToRemove));
   2271                     }
   2272 
   2273                     // Remove any empty folder
   2274                     for (long folderId : LauncherAppState.getLauncherProvider()
   2275                             .deleteEmptyFolders()) {
   2276                         sBgWorkspaceItems.remove(sBgFolders.get(folderId));
   2277                         sBgFolders.remove(folderId);
   2278                         sBgItemsIdMap.remove(folderId);
   2279                     }
   2280                 }
   2281 
   2282                 if (restoredRows.size() > 0) {
   2283                     // Update restored items that no longer require special handling
   2284                     ContentValues values = new ContentValues();
   2285                     values.put(LauncherSettings.Favorites.RESTORED, 0);
   2286                     contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
   2287                             Utilities.createDbSelectionQuery(
   2288                                     LauncherSettings.Favorites._ID, restoredRows), null);
   2289                 }
   2290 
   2291                 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
   2292                     context.registerReceiver(new AppsAvailabilityCheck(),
   2293                             new IntentFilter(StartupReceiver.SYSTEM_READY),
   2294                             null, sWorker);
   2295                 }
   2296 
   2297                 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
   2298 
   2299                 // Remove any empty screens
   2300                 ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
   2301                 for (ItemInfo item: sBgItemsIdMap) {
   2302                     long screenId = item.screenId;
   2303                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   2304                             unusedScreens.contains(screenId)) {
   2305                         unusedScreens.remove(screenId);
   2306                     }
   2307                 }
   2308 
   2309                 // If there are any empty screens remove them, and update.
   2310                 if (unusedScreens.size() != 0) {
   2311                     sBgWorkspaceScreens.removeAll(unusedScreens);
   2312                     updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
   2313                 }
   2314 
   2315                 if (DEBUG_LOADERS) {
   2316                     Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
   2317                     Log.d(TAG, "workspace layout: ");
   2318                     int nScreens = occupied.size();
   2319                     for (int y = 0; y < countY; y++) {
   2320                         String line = "";
   2321 
   2322                         for (int i = 0; i < nScreens; i++) {
   2323                             long screenId = occupied.keyAt(i);
   2324                             if (screenId > 0) {
   2325                                 line += " | ";
   2326                             }
   2327                             ItemInfo[][] screen = occupied.valueAt(i);
   2328                             for (int x = 0; x < countX; x++) {
   2329                                 if (x < screen.length && y < screen[x].length) {
   2330                                     line += (screen[x][y] != null) ? "#" : ".";
   2331                                 } else {
   2332                                     line += "!";
   2333                                 }
   2334                             }
   2335                         }
   2336                         Log.d(TAG, "[ " + line + " ]");
   2337                     }
   2338                 }
   2339             }
   2340         }
   2341 
   2342         /**
   2343          * Partially updates the item without any notification. Must be called on the worker thread.
   2344          */
   2345         private void updateItem(long itemId, ContentValues update) {
   2346             mContext.getContentResolver().update(
   2347                     LauncherSettings.Favorites.CONTENT_URI,
   2348                     update,
   2349                     BaseColumns._ID + "= ?",
   2350                     new String[]{Long.toString(itemId)});
   2351         }
   2352 
   2353         /** Filters the set of items who are directly or indirectly (via another container) on the
   2354          * specified screen. */
   2355         private void filterCurrentWorkspaceItems(long currentScreenId,
   2356                 ArrayList<ItemInfo> allWorkspaceItems,
   2357                 ArrayList<ItemInfo> currentScreenItems,
   2358                 ArrayList<ItemInfo> otherScreenItems) {
   2359             // Purge any null ItemInfos
   2360             Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
   2361             while (iter.hasNext()) {
   2362                 ItemInfo i = iter.next();
   2363                 if (i == null) {
   2364                     iter.remove();
   2365                 }
   2366             }
   2367 
   2368             // Order the set of items by their containers first, this allows use to walk through the
   2369             // list sequentially, build up a list of containers that are in the specified screen,
   2370             // as well as all items in those containers.
   2371             Set<Long> itemsOnScreen = new HashSet<Long>();
   2372             Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
   2373                 @Override
   2374                 public int compare(ItemInfo lhs, ItemInfo rhs) {
   2375                     return (int) (lhs.container - rhs.container);
   2376                 }
   2377             });
   2378             for (ItemInfo info : allWorkspaceItems) {
   2379                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   2380                     if (info.screenId == currentScreenId) {
   2381                         currentScreenItems.add(info);
   2382                         itemsOnScreen.add(info.id);
   2383                     } else {
   2384                         otherScreenItems.add(info);
   2385                     }
   2386                 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   2387                     currentScreenItems.add(info);
   2388                     itemsOnScreen.add(info.id);
   2389                 } else {
   2390                     if (itemsOnScreen.contains(info.container)) {
   2391                         currentScreenItems.add(info);
   2392                         itemsOnScreen.add(info.id);
   2393                     } else {
   2394                         otherScreenItems.add(info);
   2395                     }
   2396                 }
   2397             }
   2398         }
   2399 
   2400         /** Filters the set of widgets which are on the specified screen. */
   2401         private void filterCurrentAppWidgets(long currentScreenId,
   2402                 ArrayList<LauncherAppWidgetInfo> appWidgets,
   2403                 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
   2404                 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
   2405 
   2406             for (LauncherAppWidgetInfo widget : appWidgets) {
   2407                 if (widget == null) continue;
   2408                 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   2409                         widget.screenId == currentScreenId) {
   2410                     currentScreenWidgets.add(widget);
   2411                 } else {
   2412                     otherScreenWidgets.add(widget);
   2413                 }
   2414             }
   2415         }
   2416 
   2417         /** Filters the set of folders which are on the specified screen. */
   2418         private void filterCurrentFolders(long currentScreenId,
   2419                 LongArrayMap<ItemInfo> itemsIdMap,
   2420                 LongArrayMap<FolderInfo> folders,
   2421                 LongArrayMap<FolderInfo> currentScreenFolders,
   2422                 LongArrayMap<FolderInfo> otherScreenFolders) {
   2423 
   2424             int total = folders.size();
   2425             for (int i = 0; i < total; i++) {
   2426                 long id = folders.keyAt(i);
   2427                 FolderInfo folder = folders.valueAt(i);
   2428 
   2429                 ItemInfo info = itemsIdMap.get(id);
   2430                 if (info == null || folder == null) continue;
   2431                 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
   2432                         info.screenId == currentScreenId) {
   2433                     currentScreenFolders.put(id, folder);
   2434                 } else {
   2435                     otherScreenFolders.put(id, folder);
   2436                 }
   2437             }
   2438         }
   2439 
   2440         /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
   2441          * right) */
   2442         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
   2443             final LauncherAppState app = LauncherAppState.getInstance();
   2444             final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
   2445             // XXX: review this
   2446             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
   2447                 @Override
   2448                 public int compare(ItemInfo lhs, ItemInfo rhs) {
   2449                     int cellCountX = (int) profile.numColumns;
   2450                     int cellCountY = (int) profile.numRows;
   2451                     int screenOffset = cellCountX * cellCountY;
   2452                     int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
   2453                     long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
   2454                             lhs.cellY * cellCountX + lhs.cellX);
   2455                     long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
   2456                             rhs.cellY * cellCountX + rhs.cellX);
   2457                     return (int) (lr - rr);
   2458                 }
   2459             });
   2460         }
   2461 
   2462         private void bindWorkspaceScreens(final Callbacks oldCallbacks,
   2463                 final ArrayList<Long> orderedScreens) {
   2464             final Runnable r = new Runnable() {
   2465                 @Override
   2466                 public void run() {
   2467                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2468                     if (callbacks != null) {
   2469                         callbacks.bindScreens(orderedScreens);
   2470                     }
   2471                 }
   2472             };
   2473             runOnMainThread(r);
   2474         }
   2475 
   2476         private void bindWorkspaceItems(final Callbacks oldCallbacks,
   2477                 final ArrayList<ItemInfo> workspaceItems,
   2478                 final ArrayList<LauncherAppWidgetInfo> appWidgets,
   2479                 final LongArrayMap<FolderInfo> folders,
   2480                 ArrayList<Runnable> deferredBindRunnables) {
   2481 
   2482             final boolean postOnMainThread = (deferredBindRunnables != null);
   2483 
   2484             // Bind the workspace items
   2485             int N = workspaceItems.size();
   2486             for (int i = 0; i < N; i += ITEMS_CHUNK) {
   2487                 final int start = i;
   2488                 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
   2489                 final Runnable r = new Runnable() {
   2490                     @Override
   2491                     public void run() {
   2492                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2493                         if (callbacks != null) {
   2494                             callbacks.bindItems(workspaceItems, start, start+chunkSize,
   2495                                     false);
   2496                         }
   2497                     }
   2498                 };
   2499                 if (postOnMainThread) {
   2500                     synchronized (deferredBindRunnables) {
   2501                         deferredBindRunnables.add(r);
   2502                     }
   2503                 } else {
   2504                     runOnMainThread(r);
   2505                 }
   2506             }
   2507 
   2508             // Bind the folders
   2509             if (!folders.isEmpty()) {
   2510                 final Runnable r = new Runnable() {
   2511                     public void run() {
   2512                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2513                         if (callbacks != null) {
   2514                             callbacks.bindFolders(folders);
   2515                         }
   2516                     }
   2517                 };
   2518                 if (postOnMainThread) {
   2519                     synchronized (deferredBindRunnables) {
   2520                         deferredBindRunnables.add(r);
   2521                     }
   2522                 } else {
   2523                     runOnMainThread(r);
   2524                 }
   2525             }
   2526 
   2527             // Bind the widgets, one at a time
   2528             N = appWidgets.size();
   2529             for (int i = 0; i < N; i++) {
   2530                 final LauncherAppWidgetInfo widget = appWidgets.get(i);
   2531                 final Runnable r = new Runnable() {
   2532                     public void run() {
   2533                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2534                         if (callbacks != null) {
   2535                             callbacks.bindAppWidget(widget);
   2536                         }
   2537                     }
   2538                 };
   2539                 if (postOnMainThread) {
   2540                     deferredBindRunnables.add(r);
   2541                 } else {
   2542                     runOnMainThread(r);
   2543                 }
   2544             }
   2545         }
   2546 
   2547         /**
   2548          * Binds all loaded data to actual views on the main thread.
   2549          */
   2550         private void bindWorkspace(int synchronizeBindPage) {
   2551             final long t = SystemClock.uptimeMillis();
   2552             Runnable r;
   2553 
   2554             // Don't use these two variables in any of the callback runnables.
   2555             // Otherwise we hold a reference to them.
   2556             final Callbacks oldCallbacks = mCallbacks.get();
   2557             if (oldCallbacks == null) {
   2558                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   2559                 Log.w(TAG, "LoaderTask running with no launcher");
   2560                 return;
   2561             }
   2562 
   2563             // Save a copy of all the bg-thread collections
   2564             ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
   2565             ArrayList<LauncherAppWidgetInfo> appWidgets =
   2566                     new ArrayList<LauncherAppWidgetInfo>();
   2567             ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
   2568 
   2569             final LongArrayMap<FolderInfo> folders;
   2570             final LongArrayMap<ItemInfo> itemsIdMap;
   2571 
   2572             synchronized (sBgLock) {
   2573                 workspaceItems.addAll(sBgWorkspaceItems);
   2574                 appWidgets.addAll(sBgAppWidgets);
   2575                 orderedScreenIds.addAll(sBgWorkspaceScreens);
   2576 
   2577                 folders = sBgFolders.clone();
   2578                 itemsIdMap = sBgItemsIdMap.clone();
   2579             }
   2580 
   2581             final boolean isLoadingSynchronously =
   2582                     synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
   2583             int currScreen = isLoadingSynchronously ? synchronizeBindPage :
   2584                 oldCallbacks.getCurrentWorkspaceScreen();
   2585             if (currScreen >= orderedScreenIds.size()) {
   2586                 // There may be no workspace screens (just hotseat items and an empty page).
   2587                 currScreen = PagedView.INVALID_RESTORE_PAGE;
   2588             }
   2589             final int currentScreen = currScreen;
   2590             final long currentScreenId = currentScreen < 0
   2591                     ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
   2592 
   2593             // Load all the items that are on the current page first (and in the process, unbind
   2594             // all the existing workspace items before we call startBinding() below.
   2595             unbindWorkspaceItemsOnMainThread();
   2596 
   2597             // Separate the items that are on the current screen, and all the other remaining items
   2598             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
   2599             ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
   2600             ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
   2601                     new ArrayList<LauncherAppWidgetInfo>();
   2602             ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
   2603                     new ArrayList<LauncherAppWidgetInfo>();
   2604             LongArrayMap<FolderInfo> currentFolders = new LongArrayMap<>();
   2605             LongArrayMap<FolderInfo> otherFolders = new LongArrayMap<>();
   2606 
   2607             filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
   2608                     otherWorkspaceItems);
   2609             filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
   2610                     otherAppWidgets);
   2611             filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
   2612                     otherFolders);
   2613             sortWorkspaceItemsSpatially(currentWorkspaceItems);
   2614             sortWorkspaceItemsSpatially(otherWorkspaceItems);
   2615 
   2616             // Tell the workspace that we're about to start binding items
   2617             r = new Runnable() {
   2618                 public void run() {
   2619                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2620                     if (callbacks != null) {
   2621                         callbacks.startBinding();
   2622                     }
   2623                 }
   2624             };
   2625             runOnMainThread(r);
   2626 
   2627             bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
   2628 
   2629             // Load items on the current page
   2630             bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
   2631                     currentFolders, null);
   2632             if (isLoadingSynchronously) {
   2633                 r = new Runnable() {
   2634                     public void run() {
   2635                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2636                         if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
   2637                             callbacks.onPageBoundSynchronously(currentScreen);
   2638                         }
   2639                     }
   2640                 };
   2641                 runOnMainThread(r);
   2642             }
   2643 
   2644             // Load all the remaining pages (if we are loading synchronously, we want to defer this
   2645             // work until after the first render)
   2646             synchronized (mDeferredBindRunnables) {
   2647                 mDeferredBindRunnables.clear();
   2648             }
   2649             bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
   2650                     (isLoadingSynchronously ? mDeferredBindRunnables : null));
   2651 
   2652             // Tell the workspace that we're done binding items
   2653             r = new Runnable() {
   2654                 public void run() {
   2655                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2656                     if (callbacks != null) {
   2657                         callbacks.finishBindingItems();
   2658                     }
   2659 
   2660                     // If we're profiling, ensure this is the last thing in the queue.
   2661                     if (DEBUG_LOADERS) {
   2662                         Log.d(TAG, "bound workspace in "
   2663                             + (SystemClock.uptimeMillis()-t) + "ms");
   2664                     }
   2665 
   2666                     mIsLoadingAndBindingWorkspace = false;
   2667                 }
   2668             };
   2669             if (isLoadingSynchronously) {
   2670                 synchronized (mDeferredBindRunnables) {
   2671                     mDeferredBindRunnables.add(r);
   2672                 }
   2673             } else {
   2674                 runOnMainThread(r);
   2675             }
   2676         }
   2677 
   2678         private void loadAndBindAllApps() {
   2679             if (DEBUG_LOADERS) {
   2680                 Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
   2681             }
   2682             if (!mAllAppsLoaded) {
   2683                 loadAllApps();
   2684                 synchronized (LoaderTask.this) {
   2685                     if (mStopped) {
   2686                         return;
   2687                     }
   2688                 }
   2689                 updateIconCache();
   2690                 synchronized (LoaderTask.this) {
   2691                     if (mStopped) {
   2692                         return;
   2693                     }
   2694                     mAllAppsLoaded = true;
   2695                 }
   2696             } else {
   2697                 onlyBindAllApps();
   2698             }
   2699         }
   2700 
   2701         private void updateIconCache() {
   2702             // Ignore packages which have a promise icon.
   2703             HashSet<String> packagesToIgnore = new HashSet<>();
   2704             synchronized (sBgLock) {
   2705                 for (ItemInfo info : sBgItemsIdMap) {
   2706                     if (info instanceof ShortcutInfo) {
   2707                         ShortcutInfo si = (ShortcutInfo) info;
   2708                         if (si.isPromise() && si.getTargetComponent() != null) {
   2709                             packagesToIgnore.add(si.getTargetComponent().getPackageName());
   2710                         }
   2711                     } else if (info instanceof LauncherAppWidgetInfo) {
   2712                         LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
   2713                         if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
   2714                             packagesToIgnore.add(lawi.providerName.getPackageName());
   2715                         }
   2716                     }
   2717                 }
   2718             }
   2719             mIconCache.updateDbIcons(packagesToIgnore);
   2720         }
   2721 
   2722         private void onlyBindAllApps() {
   2723             final Callbacks oldCallbacks = mCallbacks.get();
   2724             if (oldCallbacks == null) {
   2725                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   2726                 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
   2727                 return;
   2728             }
   2729 
   2730             // shallow copy
   2731             @SuppressWarnings("unchecked")
   2732             final ArrayList<AppInfo> list
   2733                     = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
   2734             final WidgetsModel widgetList = mBgWidgetsModel.clone();
   2735             Runnable r = new Runnable() {
   2736                 public void run() {
   2737                     final long t = SystemClock.uptimeMillis();
   2738                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2739                     if (callbacks != null) {
   2740                         callbacks.bindAllApplications(list);
   2741                         callbacks.bindAllPackages(widgetList);
   2742                     }
   2743                     if (DEBUG_LOADERS) {
   2744                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
   2745                                 + (SystemClock.uptimeMillis()-t) + "ms");
   2746                     }
   2747                 }
   2748             };
   2749             boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
   2750             if (isRunningOnMainThread) {
   2751                 r.run();
   2752             } else {
   2753                 mHandler.post(r);
   2754             }
   2755         }
   2756 
   2757         private void loadAllApps() {
   2758             final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   2759 
   2760             final Callbacks oldCallbacks = mCallbacks.get();
   2761             if (oldCallbacks == null) {
   2762                 // This launcher has exited and nobody bothered to tell us.  Just bail.
   2763                 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
   2764                 return;
   2765             }
   2766 
   2767             final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
   2768 
   2769             // Clear the list of apps
   2770             mBgAllAppsList.clear();
   2771             for (UserHandleCompat user : profiles) {
   2772                 // Query for the set of apps
   2773                 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
   2774                 final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
   2775                 if (DEBUG_LOADERS) {
   2776                     Log.d(TAG, "getActivityList took "
   2777                             + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
   2778                     Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
   2779                 }
   2780                 // Fail if we don't have any apps
   2781                 // TODO: Fix this. Only fail for the current user.
   2782                 if (apps == null || apps.isEmpty()) {
   2783                     return;
   2784                 }
   2785 
   2786                 // Create the ApplicationInfos
   2787                 for (int i = 0; i < apps.size(); i++) {
   2788                     LauncherActivityInfoCompat app = apps.get(i);
   2789                     // This builds the icon bitmaps.
   2790                     mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
   2791                 }
   2792 
   2793                 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
   2794                 if (heuristic != null) {
   2795                     runAfterBindCompletes(new Runnable() {
   2796 
   2797                         @Override
   2798                         public void run() {
   2799                             heuristic.processUserApps(apps);
   2800                         }
   2801                     });
   2802                 }
   2803             }
   2804             // Huh? Shouldn't this be inside the Runnable below?
   2805             final ArrayList<AppInfo> added = mBgAllAppsList.added;
   2806             mBgAllAppsList.added = new ArrayList<AppInfo>();
   2807 
   2808             // Post callback on main thread
   2809             mHandler.post(new Runnable() {
   2810                 public void run() {
   2811 
   2812                     final long bindTime = SystemClock.uptimeMillis();
   2813                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
   2814                     if (callbacks != null) {
   2815                         callbacks.bindAllApplications(added);
   2816                         if (DEBUG_LOADERS) {
   2817                             Log.d(TAG, "bound " + added.size() + " apps in "
   2818                                 + (SystemClock.uptimeMillis() - bindTime) + "ms");
   2819                         }
   2820                     } else {
   2821                         Log.i(TAG, "not binding apps: no Launcher activity");
   2822                     }
   2823                 }
   2824             });
   2825             // Cleanup any data stored for a deleted user.
   2826             ManagedProfileHeuristic.processAllUsers(profiles, mContext);
   2827 
   2828             loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
   2829                     true /* refresh */);
   2830             if (DEBUG_LOADERS) {
   2831                 Log.d(TAG, "Icons processed in "
   2832                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
   2833             }
   2834         }
   2835 
   2836         public void dumpState() {
   2837             synchronized (sBgLock) {
   2838                 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
   2839                 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
   2840                 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
   2841                 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
   2842             }
   2843         }
   2844     }
   2845 
   2846     /**
   2847      * Called when the icons for packages have been updated in the icon cache.
   2848      */
   2849     public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandleCompat user) {
   2850         final Callbacks callbacks = getCallback();
   2851         final ArrayList<AppInfo> updatedApps = new ArrayList<>();
   2852         final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
   2853 
   2854         // If any package icon has changed (app was updated while launcher was dead),
   2855         // update the corresponding shortcuts.
   2856         synchronized (sBgLock) {
   2857             for (ItemInfo info : sBgItemsIdMap) {
   2858                 if (info instanceof ShortcutInfo && user.equals(info.user)
   2859                         && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
   2860                     ShortcutInfo si = (ShortcutInfo) info;
   2861                     ComponentName cn = si.getTargetComponent();
   2862                     if (cn != null && updatedPackages.contains(cn.getPackageName())) {
   2863                         si.updateIcon(mIconCache);
   2864                         updatedShortcuts.add(si);
   2865                     }
   2866                 }
   2867             }
   2868             mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
   2869         }
   2870 
   2871         if (!updatedShortcuts.isEmpty()) {
   2872             final UserHandleCompat userFinal = user;
   2873             mHandler.post(new Runnable() {
   2874 
   2875                 public void run() {
   2876                     Callbacks cb = getCallback();
   2877                     if (cb != null && callbacks == cb) {
   2878                         cb.bindShortcutsChanged(updatedShortcuts,
   2879                                 new ArrayList<ShortcutInfo>(), userFinal);
   2880                     }
   2881                 }
   2882             });
   2883         }
   2884 
   2885         if (!updatedApps.isEmpty()) {
   2886             mHandler.post(new Runnable() {
   2887 
   2888                 public void run() {
   2889                     Callbacks cb = getCallback();
   2890                     if (cb != null && callbacks == cb) {
   2891                         cb.bindAppsUpdated(updatedApps);
   2892                     }
   2893                 }
   2894             });
   2895         }
   2896 
   2897         // Reload widget list. No need to refresh, as we only want to update the icons and labels.
   2898         loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false);
   2899     }
   2900 
   2901     void enqueuePackageUpdated(PackageUpdatedTask task) {
   2902         sWorker.post(task);
   2903     }
   2904 
   2905     @Thunk class AppsAvailabilityCheck extends BroadcastReceiver {
   2906 
   2907         @Override
   2908         public void onReceive(Context context, Intent intent) {
   2909             synchronized (sBgLock) {
   2910                 final LauncherAppsCompat launcherApps = LauncherAppsCompat
   2911                         .getInstance(mApp.getContext());
   2912                 final PackageManager manager = context.getPackageManager();
   2913                 final ArrayList<String> packagesRemoved = new ArrayList<String>();
   2914                 final ArrayList<String> packagesUnavailable = new ArrayList<String>();
   2915                 for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
   2916                     UserHandleCompat user = entry.getKey();
   2917                     packagesRemoved.clear();
   2918                     packagesUnavailable.clear();
   2919                     for (String pkg : entry.getValue()) {
   2920                         if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
   2921                             boolean packageOnSdcard = launcherApps.isAppEnabled(
   2922                                     manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
   2923                             if (packageOnSdcard) {
   2924                                 Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
   2925                                 packagesUnavailable.add(pkg);
   2926                             } else {
   2927                                 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
   2928                                 packagesRemoved.add(pkg);
   2929                             }
   2930                         }
   2931                     }
   2932                     if (!packagesRemoved.isEmpty()) {
   2933                         enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
   2934                                 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
   2935                     }
   2936                     if (!packagesUnavailable.isEmpty()) {
   2937                         enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
   2938                                 packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
   2939                     }
   2940                 }
   2941                 sPendingPackages.clear();
   2942             }
   2943         }
   2944     }
   2945 
   2946     private class PackageUpdatedTask implements Runnable {
   2947         int mOp;
   2948         String[] mPackages;
   2949         UserHandleCompat mUser;
   2950 
   2951         public static final int OP_NONE = 0;
   2952         public static final int OP_ADD = 1;
   2953         public static final int OP_UPDATE = 2;
   2954         public static final int OP_REMOVE = 3; // uninstlled
   2955         public static final int OP_UNAVAILABLE = 4; // external media unmounted
   2956 
   2957 
   2958         public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
   2959             mOp = op;
   2960             mPackages = packages;
   2961             mUser = user;
   2962         }
   2963 
   2964         public void run() {
   2965             if (!mHasLoaderCompletedOnce) {
   2966                 // Loader has not yet run.
   2967                 return;
   2968             }
   2969             final Context context = mApp.getContext();
   2970 
   2971             final String[] packages = mPackages;
   2972             final int N = packages.length;
   2973             switch (mOp) {
   2974                 case OP_ADD: {
   2975                     for (int i=0; i<N; i++) {
   2976                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
   2977                         mIconCache.updateIconsForPkg(packages[i], mUser);
   2978                         mBgAllAppsList.addPackage(context, packages[i], mUser);
   2979                     }
   2980 
   2981                     ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
   2982                     if (heuristic != null) {
   2983                         heuristic.processPackageAdd(mPackages);
   2984                     }
   2985                     break;
   2986                 }
   2987                 case OP_UPDATE:
   2988                     for (int i=0; i<N; i++) {
   2989                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
   2990                         mIconCache.updateIconsForPkg(packages[i], mUser);
   2991                         mBgAllAppsList.updatePackage(context, packages[i], mUser);
   2992                         mApp.getWidgetCache().removePackage(packages[i], mUser);
   2993                     }
   2994                     break;
   2995                 case OP_REMOVE: {
   2996                     ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
   2997                     if (heuristic != null) {
   2998                         heuristic.processPackageRemoved(mPackages);
   2999                     }
   3000                     for (int i=0; i<N; i++) {
   3001                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
   3002                         mIconCache.removeIconsForPkg(packages[i], mUser);
   3003                     }
   3004                     // Fall through
   3005                 }
   3006                 case OP_UNAVAILABLE:
   3007                     for (int i=0; i<N; i++) {
   3008                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
   3009                         mBgAllAppsList.removePackage(packages[i], mUser);
   3010                         mApp.getWidgetCache().removePackage(packages[i], mUser);
   3011                     }
   3012                     break;
   3013             }
   3014 
   3015             ArrayList<AppInfo> added = null;
   3016             ArrayList<AppInfo> modified = null;
   3017             final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
   3018 
   3019             if (mBgAllAppsList.added.size() > 0) {
   3020                 added = new ArrayList<AppInfo>(mBgAllAppsList.added);
   3021                 mBgAllAppsList.added.clear();
   3022             }
   3023             if (mBgAllAppsList.modified.size() > 0) {
   3024                 modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
   3025                 mBgAllAppsList.modified.clear();
   3026             }
   3027             if (mBgAllAppsList.removed.size() > 0) {
   3028                 removedApps.addAll(mBgAllAppsList.removed);
   3029                 mBgAllAppsList.removed.clear();
   3030             }
   3031 
   3032             final Callbacks callbacks = getCallback();
   3033             if (callbacks == null) {
   3034                 Log.w(TAG, "Nobody to tell about the new app.  Launcher is probably loading.");
   3035                 return;
   3036             }
   3037 
   3038             final HashMap<ComponentName, AppInfo> addedOrUpdatedApps =
   3039                     new HashMap<ComponentName, AppInfo>();
   3040 
   3041             if (added != null) {
   3042                 addAppsToAllApps(context, added);
   3043                 for (AppInfo ai : added) {
   3044                     addedOrUpdatedApps.put(ai.componentName, ai);
   3045                 }
   3046             }
   3047 
   3048             if (modified != null) {
   3049                 final ArrayList<AppInfo> modifiedFinal = modified;
   3050                 for (AppInfo ai : modified) {
   3051                     addedOrUpdatedApps.put(ai.componentName, ai);
   3052                 }
   3053 
   3054                 mHandler.post(new Runnable() {
   3055                     public void run() {
   3056                         Callbacks cb = getCallback();
   3057                         if (callbacks == cb && cb != null) {
   3058                             callbacks.bindAppsUpdated(modifiedFinal);
   3059                         }
   3060                     }
   3061                 });
   3062             }
   3063 
   3064             // Update shortcut infos
   3065             if (mOp == OP_ADD || mOp == OP_UPDATE) {
   3066                 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
   3067                 final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
   3068                 final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
   3069 
   3070                 HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
   3071                 synchronized (sBgLock) {
   3072                     for (ItemInfo info : sBgItemsIdMap) {
   3073                         if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
   3074                             ShortcutInfo si = (ShortcutInfo) info;
   3075                             boolean infoUpdated = false;
   3076                             boolean shortcutUpdated = false;
   3077 
   3078                             // Update shortcuts which use iconResource.
   3079                             if ((si.iconResource != null)
   3080                                     && packageSet.contains(si.iconResource.packageName)) {
   3081                                 Bitmap icon = Utilities.createIconBitmap(
   3082                                         si.iconResource.packageName,
   3083                                         si.iconResource.resourceName, context);
   3084                                 if (icon != null) {
   3085                                     si.setIcon(icon);
   3086                                     si.usingFallbackIcon = false;
   3087                                     infoUpdated = true;
   3088                                 }
   3089                             }
   3090 
   3091                             ComponentName cn = si.getTargetComponent();
   3092                             if (cn != null && packageSet.contains(cn.getPackageName())) {
   3093                                 AppInfo appInfo = addedOrUpdatedApps.get(cn);
   3094 
   3095                                 if (si.isPromise()) {
   3096                                     if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
   3097                                         // Auto install icon
   3098                                         PackageManager pm = context.getPackageManager();
   3099                                         ResolveInfo matched = pm.resolveActivity(
   3100                                                 new Intent(Intent.ACTION_MAIN)
   3101                                                 .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
   3102                                                 PackageManager.MATCH_DEFAULT_ONLY);
   3103                                         if (matched == null) {
   3104                                             // Try to find the best match activity.
   3105                                             Intent intent = pm.getLaunchIntentForPackage(
   3106                                                     cn.getPackageName());
   3107                                             if (intent != null) {
   3108                                                 cn = intent.getComponent();
   3109                                                 appInfo = addedOrUpdatedApps.get(cn);
   3110                                             }
   3111 
   3112                                             if ((intent == null) || (appInfo == null)) {
   3113                                                 removedShortcuts.add(si);
   3114                                                 continue;
   3115                                             }
   3116                                             si.promisedIntent = intent;
   3117                                         }
   3118                                     }
   3119 
   3120                                     // Restore the shortcut.
   3121                                     if (appInfo != null) {
   3122                                         si.flags = appInfo.flags;
   3123                                     }
   3124 
   3125                                     si.intent = si.promisedIntent;
   3126                                     si.promisedIntent = null;
   3127                                     si.status = ShortcutInfo.DEFAULT;
   3128                                     infoUpdated = true;
   3129                                     si.updateIcon(mIconCache);
   3130                                 }
   3131 
   3132                                 if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
   3133                                         && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
   3134                                     si.updateIcon(mIconCache);
   3135                                     si.title = Utilities.trim(appInfo.title);
   3136                                     si.contentDescription = appInfo.contentDescription;
   3137                                     infoUpdated = true;
   3138                                 }
   3139 
   3140                                 if ((si.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
   3141                                     // Since package was just updated, the target must be available now.
   3142                                     si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
   3143                                     shortcutUpdated = true;
   3144                                 }
   3145                             }
   3146 
   3147                             if (infoUpdated || shortcutUpdated) {
   3148                                 updatedShortcuts.add(si);
   3149                             }
   3150                             if (infoUpdated) {
   3151                                 updateItemInDatabase(context, si);
   3152                             }
   3153                         } else if (info instanceof LauncherAppWidgetInfo) {
   3154                             LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
   3155                             if (mUser.equals(widgetInfo.user)
   3156                                     && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
   3157                                     && packageSet.contains(widgetInfo.providerName.getPackageName())) {
   3158                                 widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
   3159                                 widgets.add(widgetInfo);
   3160                                 updateItemInDatabase(context, widgetInfo);
   3161                             }
   3162                         }
   3163                     }
   3164                 }
   3165 
   3166                 if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
   3167                     mHandler.post(new Runnable() {
   3168 
   3169                         public void run() {
   3170                             Callbacks cb = getCallback();
   3171                             if (callbacks == cb && cb != null) {
   3172                                 callbacks.bindShortcutsChanged(
   3173                                         updatedShortcuts, removedShortcuts, mUser);
   3174                             }
   3175                         }
   3176                     });
   3177                     if (!removedShortcuts.isEmpty()) {
   3178                         deleteItemsFromDatabase(context, removedShortcuts);
   3179                     }
   3180                 }
   3181                 if (!widgets.isEmpty()) {
   3182                     mHandler.post(new Runnable() {
   3183                         public void run() {
   3184                             Callbacks cb = getCallback();
   3185                             if (callbacks == cb && cb != null) {
   3186                                 callbacks.bindWidgetsRestored(widgets);
   3187                             }
   3188                         }
   3189                     });
   3190                 }
   3191             }
   3192 
   3193             final ArrayList<String> removedPackageNames =
   3194                     new ArrayList<String>();
   3195             if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
   3196                 // Mark all packages in the broadcast to be removed
   3197                 removedPackageNames.addAll(Arrays.asList(packages));
   3198             } else if (mOp == OP_UPDATE) {
   3199                 // Mark disabled packages in the broadcast to be removed
   3200                 for (int i=0; i<N; i++) {
   3201                     if (isPackageDisabled(context, packages[i], mUser)) {
   3202                         removedPackageNames.add(packages[i]);
   3203                     }
   3204                 }
   3205             }
   3206 
   3207             if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
   3208                 final int removeReason;
   3209                 if (mOp == OP_UNAVAILABLE) {
   3210                     removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
   3211                 } else {
   3212                     // Remove all the components associated with this package
   3213                     for (String pn : removedPackageNames) {
   3214                         deletePackageFromDatabase(context, pn, mUser);
   3215                     }
   3216                     // Remove all the specific components
   3217                     for (AppInfo a : removedApps) {
   3218                         ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
   3219                         deleteItemsFromDatabase(context, infos);
   3220                     }
   3221                     removeReason = 0;
   3222                 }
   3223 
   3224                 // Remove any queued items from the install queue
   3225                 InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
   3226                 // Call the components-removed callback
   3227                 mHandler.post(new Runnable() {
   3228                     public void run() {
   3229                         Callbacks cb = getCallback();
   3230                         if (callbacks == cb && cb != null) {
   3231                             callbacks.bindComponentsRemoved(
   3232                                     removedPackageNames, removedApps, mUser, removeReason);
   3233                         }
   3234                     }
   3235                 });
   3236             }
   3237 
   3238             // onProvidersChanged method (API >= 17) already refreshed the widget list
   3239             loadAndBindWidgetsAndShortcuts(context, callbacks, Build.VERSION.SDK_INT < 17);
   3240 
   3241             // Write all the logs to disk
   3242             mHandler.post(new Runnable() {
   3243                 public void run() {
   3244                     Callbacks cb = getCallback();
   3245                     if (callbacks == cb && cb != null) {
   3246                         callbacks.dumpLogsToLocalData();
   3247                     }
   3248                 }
   3249             });
   3250         }
   3251     }
   3252 
   3253     public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
   3254             boolean refresh) {
   3255         ArrayList<LauncherAppWidgetProviderInfo> results =
   3256                 new ArrayList<LauncherAppWidgetProviderInfo>();
   3257         try {
   3258             synchronized (sBgLock) {
   3259                 if (sBgWidgetProviders == null || refresh) {
   3260                     HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
   3261                             = new HashMap<>();
   3262                     AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
   3263                     LauncherAppWidgetProviderInfo info;
   3264 
   3265                     List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
   3266                     for (AppWidgetProviderInfo pInfo : widgets) {
   3267                         info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
   3268                         UserHandleCompat user = wm.getUser(info);
   3269                         tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
   3270                     }
   3271 
   3272                     Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
   3273                     for (CustomAppWidget widget : customWidgets) {
   3274                         info = new LauncherAppWidgetProviderInfo(context, widget);
   3275                         UserHandleCompat user = wm.getUser(info);
   3276                         tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
   3277                     }
   3278                     // Replace the global list at the very end, so that if there is an exception,
   3279                     // previously loaded provider list is used.
   3280                     sBgWidgetProviders = tmpWidgetProviders;
   3281                 }
   3282                 results.addAll(sBgWidgetProviders.values());
   3283                 return results;
   3284             }
   3285         } catch (Exception e) {
   3286             if (e.getCause() instanceof TransactionTooLargeException) {
   3287                 // the returned value may be incomplete and will not be refreshed until the next
   3288                 // time Launcher starts.
   3289                 // TODO: after figuring out a repro step, introduce a dirty bit to check when
   3290                 // onResume is called to refresh the widget provider list.
   3291                 synchronized (sBgLock) {
   3292                     if (sBgWidgetProviders != null) {
   3293                         results.addAll(sBgWidgetProviders.values());
   3294                     }
   3295                     return results;
   3296                 }
   3297             } else {
   3298                 throw e;
   3299             }
   3300         }
   3301     }
   3302 
   3303     public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name,
   3304             UserHandleCompat user) {
   3305         synchronized (sBgLock) {
   3306             if (sBgWidgetProviders == null) {
   3307                 getWidgetProviders(ctx, false /* refresh */);
   3308             }
   3309             return sBgWidgetProviders.get(new ComponentKey(name, user));
   3310         }
   3311     }
   3312 
   3313     public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks,
   3314             final boolean refresh) {
   3315 
   3316         runOnWorkerThread(new Runnable() {
   3317             @Override
   3318             public void run() {
   3319                 updateWidgetsModel(context, refresh);
   3320                 final WidgetsModel model = mBgWidgetsModel.clone();
   3321 
   3322                 mHandler.post(new Runnable() {
   3323                     @Override
   3324                     public void run() {
   3325                         Callbacks cb = getCallback();
   3326                         if (callbacks == cb && cb != null) {
   3327                             callbacks.bindAllPackages(model);
   3328                         }
   3329                     }
   3330                 });
   3331                 // update the Widget entries inside DB on the worker thread.
   3332                 LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
   3333                         model.getRawList());
   3334             }
   3335         });
   3336     }
   3337 
   3338     /**
   3339      * Returns a list of ResolveInfos/AppWidgetInfos.
   3340      *
   3341      * @see #loadAndBindWidgetsAndShortcuts
   3342      */
   3343     @Thunk void updateWidgetsModel(Context context, boolean refresh) {
   3344         PackageManager packageManager = context.getPackageManager();
   3345         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
   3346         widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
   3347         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
   3348         widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
   3349         mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
   3350     }
   3351 
   3352     @Thunk static boolean isPackageDisabled(Context context, String packageName,
   3353             UserHandleCompat user) {
   3354         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
   3355         return !launcherApps.isPackageEnabledForProfile(packageName, user);
   3356     }
   3357 
   3358     public static boolean isValidPackageActivity(Context context, ComponentName cn,
   3359             UserHandleCompat user) {
   3360         if (cn == null) {
   3361             return false;
   3362         }
   3363         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
   3364         if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
   3365             return false;
   3366         }
   3367         return launcherApps.isActivityEnabledForProfile(cn, user);
   3368     }
   3369 
   3370     public static boolean isValidPackage(Context context, String packageName,
   3371             UserHandleCompat user) {
   3372         if (packageName == null) {
   3373             return false;
   3374         }
   3375         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
   3376         return launcherApps.isPackageEnabledForProfile(packageName, user);
   3377     }
   3378 
   3379     /**
   3380      * Make an ShortcutInfo object for a restored application or shortcut item that points
   3381      * to a package that is not yet installed on the system.
   3382      */
   3383     public ShortcutInfo getRestoredItemInfo(Cursor c, int titleIndex, Intent intent,
   3384             int promiseType, int itemType, CursorIconInfo iconInfo, Context context) {
   3385         final ShortcutInfo info = new ShortcutInfo();
   3386         info.user = UserHandleCompat.myUserHandle();
   3387 
   3388         Bitmap icon = iconInfo.loadIcon(c, info, context);
   3389         // the fallback icon
   3390         if (icon == null) {
   3391             mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */);
   3392         } else {
   3393             info.setIcon(icon);
   3394         }
   3395 
   3396         if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
   3397             String title = (c != null) ? c.getString(titleIndex) : null;
   3398             if (!TextUtils.isEmpty(title)) {
   3399                 info.title = Utilities.trim(title);
   3400             }
   3401         } else if  ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
   3402             if (TextUtils.isEmpty(info.title)) {
   3403                 info.title = (c != null) ? Utilities.trim(c.getString(titleIndex)) : "";
   3404             }
   3405         } else {
   3406             throw new InvalidParameterException("Invalid restoreType " + promiseType);
   3407         }
   3408 
   3409         info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
   3410         info.itemType = itemType;
   3411         info.promisedIntent = intent;
   3412         info.status = promiseType;
   3413         return info;
   3414     }
   3415 
   3416     /**
   3417      * Make an Intent object for a restored application or shortcut item that points
   3418      * to the market page for the item.
   3419      */
   3420     @Thunk Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
   3421         ComponentName componentName = intent.getComponent();
   3422         return getMarketIntent(componentName.getPackageName());
   3423     }
   3424 
   3425     static Intent getMarketIntent(String packageName) {
   3426         return new Intent(Intent.ACTION_VIEW)
   3427             .setData(new Uri.Builder()
   3428                 .scheme("market")
   3429                 .authority("details")
   3430                 .appendQueryParameter("id", packageName)
   3431                 .build());
   3432     }
   3433 
   3434     /**
   3435      * Make an ShortcutInfo object for a shortcut that is an application.
   3436      *
   3437      * If c is not null, then it will be used to fill in missing data like the title and icon.
   3438      */
   3439     public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
   3440             UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
   3441             boolean allowMissingTarget, boolean useLowResIcon) {
   3442         if (user == null) {
   3443             Log.d(TAG, "Null user found in getShortcutInfo");
   3444             return null;
   3445         }
   3446 
   3447         ComponentName componentName = intent.getComponent();
   3448         if (componentName == null) {
   3449             Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
   3450             return null;
   3451         }
   3452 
   3453         Intent newIntent = new Intent(intent.getAction(), null);
   3454         newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
   3455         newIntent.setComponent(componentName);
   3456         LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
   3457         if ((lai == null) && !allowMissingTarget) {
   3458             Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
   3459             return null;
   3460         }
   3461 
   3462         final ShortcutInfo info = new ShortcutInfo();
   3463         mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon);
   3464         if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) {
   3465             Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context);
   3466             info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
   3467         }
   3468 
   3469         // from the db
   3470         if (TextUtils.isEmpty(info.title) && c != null) {
   3471             info.title =  Utilities.trim(c.getString(titleIndex));
   3472         }
   3473 
   3474         // fall back to the class name of the activity
   3475         if (info.title == null) {
   3476             info.title = componentName.getClassName();
   3477         }
   3478 
   3479         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
   3480         info.user = user;
   3481         info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
   3482         if (lai != null) {
   3483             info.flags = AppInfo.initFlags(lai);
   3484         }
   3485         return info;
   3486     }
   3487 
   3488     static ArrayList<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos,
   3489             ItemInfoFilter f) {
   3490         HashSet<ItemInfo> filtered = new HashSet<ItemInfo>();
   3491         for (ItemInfo i : infos) {
   3492             if (i instanceof ShortcutInfo) {
   3493                 ShortcutInfo info = (ShortcutInfo) i;
   3494                 ComponentName cn = info.getTargetComponent();
   3495                 if (cn != null && f.filterItem(null, info, cn)) {
   3496                     filtered.add(info);
   3497                 }
   3498             } else if (i instanceof FolderInfo) {
   3499                 FolderInfo info = (FolderInfo) i;
   3500                 for (ShortcutInfo s : info.contents) {
   3501                     ComponentName cn = s.getTargetComponent();
   3502                     if (cn != null && f.filterItem(info, s, cn)) {
   3503                         filtered.add(s);
   3504                     }
   3505                 }
   3506             } else if (i instanceof LauncherAppWidgetInfo) {
   3507                 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
   3508                 ComponentName cn = info.providerName;
   3509                 if (cn != null && f.filterItem(null, info, cn)) {
   3510                     filtered.add(info);
   3511                 }
   3512             }
   3513         }
   3514         return new ArrayList<ItemInfo>(filtered);
   3515     }
   3516 
   3517     @Thunk ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
   3518             final UserHandleCompat user) {
   3519         ItemInfoFilter filter  = new ItemInfoFilter() {
   3520             @Override
   3521             public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
   3522                 if (info.user == null) {
   3523                     return cn.equals(cname);
   3524                 } else {
   3525                     return cn.equals(cname) && info.user.equals(user);
   3526                 }
   3527             }
   3528         };
   3529         return filterItemInfos(sBgItemsIdMap, filter);
   3530     }
   3531 
   3532     /**
   3533      * Make an ShortcutInfo object for a shortcut that isn't an application.
   3534      */
   3535     @Thunk ShortcutInfo getShortcutInfo(Cursor c, Context context,
   3536             int titleIndex, CursorIconInfo iconInfo) {
   3537         final ShortcutInfo info = new ShortcutInfo();
   3538         // Non-app shortcuts are only supported for current user.
   3539         info.user = UserHandleCompat.myUserHandle();
   3540         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
   3541 
   3542         // TODO: If there's an explicit component and we can't install that, delete it.
   3543 
   3544         info.title = Utilities.trim(c.getString(titleIndex));
   3545 
   3546         Bitmap icon = iconInfo.loadIcon(c, info, context);
   3547         // the fallback icon
   3548         if (icon == null) {
   3549             icon = mIconCache.getDefaultIcon(info.user);
   3550             info.usingFallbackIcon = true;
   3551         }
   3552         info.setIcon(icon);
   3553         return info;
   3554     }
   3555 
   3556     ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
   3557         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
   3558         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   3559         Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
   3560 
   3561         if (intent == null) {
   3562             // If the intent is null, we can't construct a valid ShortcutInfo, so we return null
   3563             Log.e(TAG, "Can't construct ShorcutInfo with null intent");
   3564             return null;
   3565         }
   3566 
   3567         Bitmap icon = null;
   3568         boolean customIcon = false;
   3569         ShortcutIconResource iconResource = null;
   3570 
   3571         if (bitmap instanceof Bitmap) {
   3572             icon = Utilities.createIconBitmap((Bitmap) bitmap, context);
   3573             customIcon = true;
   3574         } else {
   3575             Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
   3576             if (extra instanceof ShortcutIconResource) {
   3577                 iconResource = (ShortcutIconResource) extra;
   3578                 icon = Utilities.createIconBitmap(iconResource.packageName,
   3579                         iconResource.resourceName, context);
   3580             }
   3581         }
   3582 
   3583         final ShortcutInfo info = new ShortcutInfo();
   3584 
   3585         // Only support intents for current user for now. Intents sent from other
   3586         // users wouldn't get here without intent forwarding anyway.
   3587         info.user = UserHandleCompat.myUserHandle();
   3588         if (icon == null) {
   3589             icon = mIconCache.getDefaultIcon(info.user);
   3590             info.usingFallbackIcon = true;
   3591         }
   3592         info.setIcon(icon);
   3593 
   3594         info.title = Utilities.trim(name);
   3595         info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
   3596         info.intent = intent;
   3597         info.customIcon = customIcon;
   3598         info.iconResource = iconResource;
   3599 
   3600         return info;
   3601     }
   3602 
   3603     /**
   3604      * Return an existing FolderInfo object if we have encountered this ID previously,
   3605      * or make a new one.
   3606      */
   3607     @Thunk static FolderInfo findOrMakeFolder(LongArrayMap<FolderInfo> folders, long id) {
   3608         // See if a placeholder was created for us already
   3609         FolderInfo folderInfo = folders.get(id);
   3610         if (folderInfo == null) {
   3611             // No placeholder -- create a new instance
   3612             folderInfo = new FolderInfo();
   3613             folders.put(id, folderInfo);
   3614         }
   3615         return folderInfo;
   3616     }
   3617 
   3618 
   3619     static boolean isValidProvider(AppWidgetProviderInfo provider) {
   3620         return (provider != null) && (provider.provider != null)
   3621                 && (provider.provider.getPackageName() != null);
   3622     }
   3623 
   3624     public void dumpState() {
   3625         Log.d(TAG, "mCallbacks=" + mCallbacks);
   3626         AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
   3627         AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.added", mBgAllAppsList.added);
   3628         AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.removed", mBgAllAppsList.removed);
   3629         AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.modified", mBgAllAppsList.modified);
   3630         if (mLoaderTask != null) {
   3631             mLoaderTask.dumpState();
   3632         } else {
   3633             Log.d(TAG, "mLoaderTask=null");
   3634         }
   3635     }
   3636 
   3637     public Callbacks getCallback() {
   3638         return mCallbacks != null ? mCallbacks.get() : null;
   3639     }
   3640 
   3641     /**
   3642      * @return {@link FolderInfo} if its already loaded.
   3643      */
   3644     public FolderInfo findFolderById(Long folderId) {
   3645         synchronized (sBgLock) {
   3646             return sBgFolders.get(folderId);
   3647         }
   3648     }
   3649 
   3650     /**
   3651      * @return the looper for the worker thread which can be used to start background tasks.
   3652      */
   3653     public static Looper getWorkerLooper() {
   3654         return sWorkerThread.getLooper();
   3655     }
   3656 }
   3657