diff options
Diffstat (limited to 'src/com/android/launcher3/LauncherModel.java')
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 741 |
1 files changed, 500 insertions, 241 deletions
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index e06fe5fb3..9fe0bf3a5 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -48,6 +48,8 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.provider.BaseColumns; +import android.text.TextUtils; +import android.provider.Settings; import android.util.Log; import android.util.Pair; @@ -78,13 +80,21 @@ import java.util.concurrent.atomic.AtomicBoolean; public class LauncherModel extends BroadcastReceiver { static final boolean DEBUG_LOADERS = false; static final String TAG = "Launcher.Model"; + public static final String SETTINGS_PROTECTED_COMPONENTS = "protected_components"; // true = use a "More Apps" folder for non-workspace apps on upgrade // false = strew non-workspace apps across the workspace on upgrade public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false; + public static final int LOADER_FLAG_NONE = 0; + public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0; + public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1; + private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons + private static final long INVALID_SCREEN_ID = -1L; + private final boolean mAppsCanBeOnRemoveableStorage; + private final boolean mOldContentProviderExists; private final LauncherAppState mApp; private final Object mLock = new Object(); @@ -175,8 +185,7 @@ public class LauncherModel extends BroadcastReceiver { ArrayList<AppInfo> addedApps); public void bindAppsUpdated(ArrayList<AppInfo> apps); public void bindComponentsRemoved(ArrayList<String> packageNames, - ArrayList<AppInfo> appInfos, - boolean matchPackageNamesOnly); + ArrayList<AppInfo> appInfos); public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts); public void bindSearchablesChanged(); public void onPageBoundSynchronously(int page); @@ -188,16 +197,16 @@ public class LauncherModel extends BroadcastReceiver { } LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { - final Context context = app.getContext(); + Context context = app.getContext(); + ContentResolver contentResolver = context.getContentResolver(); mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable(); + mOldContentProviderExists = (contentResolver.acquireContentProviderClient( + LauncherSettings.Favorites.OLD_CONTENT_URI) != null); mApp = app; mBgAllAppsList = new AllAppsList(iconCache, appFilter); mIconCache = iconCache; - mDefaultIcon = Utilities.createIconBitmap( - mIconCache.getFullResDefaultActivityIcon(), context); - final Resources res = context.getResources(); Configuration config = res.getConfiguration(); mPreviousConfigMcc = config.mcc; @@ -228,6 +237,10 @@ public class LauncherModel extends BroadcastReceiver { } } + boolean canMigrateFromOldLauncherDb(Launcher launcher) { + return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; + } + static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy, long screen) { LauncherAppState app = LauncherAppState.getInstance(); @@ -289,14 +302,58 @@ public class LauncherModel extends BroadcastReceiver { return null; } - public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps, - final ArrayList<AppInfo> allAppsApps) { - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; - addAndBindAddedApps(context, workspaceApps, cb, allAppsApps); + public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) { + final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + + if (allAppsApps == null) { + throw new RuntimeException("allAppsApps must not be null"); + } + if (allAppsApps.isEmpty()) { + return; + } + + final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>(); + Iterator<AppInfo> iter = allAppsApps.iterator(); + while (iter.hasNext()) { + ItemInfo a = iter.next(); + if (LauncherModel.appWasRestored(ctx, a.getIntent())) { + restoredAppsFinal.add((AppInfo) a); + } + } + + // Process the newly added applications and add them to the database first + Runnable r = new Runnable() { + public void run() { + runOnMainThread(new Runnable() { + public void run() { + Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; + if (callbacks == cb && cb != null) { + if (!restoredAppsFinal.isEmpty()) { + for (AppInfo info : restoredAppsFinal) { + final Intent intent = info.getIntent(); + if (intent != null) { + mIconCache.deletePreloadedIcon(intent.getComponent()); + } + } + callbacks.bindAppsUpdated(restoredAppsFinal); + } + callbacks.bindAppsAdded(null, null, null, allAppsApps); + } + } + }); + } + }; + runOnWorkerThread(r); } - public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps, - final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) { - if (workspaceApps.isEmpty() && allAppsApps.isEmpty()) { + + public void addAndBindAddedWorkspaceApps(final Context context, + final ArrayList<ItemInfo> workspaceApps) { + final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null; + + if (workspaceApps == null) { + throw new RuntimeException("workspaceApps and allAppsApps must not be null"); + } + if (workspaceApps.isEmpty()) { return; } // Process the newly added applications and add them to the database first @@ -304,6 +361,7 @@ public class LauncherModel extends BroadcastReceiver { public void run() { final ArrayList<ItemInfo> addedShortcutsFinal = new ArrayList<ItemInfo>(); final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>(); + final ArrayList<AppInfo> restoredAppsFinal = new ArrayList<AppInfo>(); // Get the list of workspace screens. We need to append to this list and // can not use sBgWorkspaceScreens because loadWorkspace() may not have been @@ -324,6 +382,11 @@ public class LauncherModel extends BroadcastReceiver { // Short-circuit this logic if the icon exists somewhere on the workspace if (LauncherModel.shortcutExists(context, name, launchIntent)) { + // Only InstallShortcutReceiver sends us shortcutInfos, ignore them + if (a instanceof AppInfo && + LauncherModel.appWasRestored(context, launchIntent)) { + restoredAppsFinal.add((AppInfo) a); + } continue; } @@ -379,7 +442,7 @@ public class LauncherModel extends BroadcastReceiver { // Update the workspace screens updateWorkspaceScreenOrder(context, workspaceScreens); - if (!addedShortcutsFinal.isEmpty() || !allAppsApps.isEmpty()) { + if (!addedShortcutsFinal.isEmpty()) { runOnMainThread(new Runnable() { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; @@ -398,7 +461,10 @@ public class LauncherModel extends BroadcastReceiver { } } callbacks.bindAppsAdded(addedWorkspaceScreensFinal, - addNotAnimated, addAnimated, allAppsApps); + addNotAnimated, addAnimated, null); + if (!restoredAppsFinal.isEmpty()) { + callbacks.bindAppsUpdated(restoredAppsFinal); + } } } }); @@ -409,6 +475,11 @@ public class LauncherModel extends BroadcastReceiver { } public Bitmap getFallbackIcon() { + if (mDefaultIcon == null) { + final Context context = LauncherAppState.getInstance().getContext(); + mDefaultIcon = Utilities.createIconBitmap( + mIconCache.getFullResDefaultActivityIcon(), context); + } return Bitmap.createBitmap(mDefaultIcon); } @@ -504,8 +575,7 @@ public class LauncherModel extends BroadcastReceiver { if (stackTrace != null) { e.setStackTrace(stackTrace); } - // TODO: something breaks this in the upgrade path - //throw e; + throw e; } } @@ -589,8 +659,9 @@ public class LauncherModel extends BroadcastReceiver { // as in Workspace.onDrop. Here, we just add/remove them from the list of items // that are on the desktop, as appropriate ItemInfo modelItem = sBgItemsIdMap.get(itemId); - if (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || - modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { + if (modelItem != null && + (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || + modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) { switch (modelItem.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: @@ -760,6 +831,30 @@ public class LauncherModel extends BroadcastReceiver { } /** + * Returns true if the shortcuts already exists in the database. + * we identify a shortcut by the component name of the intent. + */ + static boolean appWasRestored(Context context, Intent intent) { + final ContentResolver cr = context.getContentResolver(); + final ComponentName component = intent.getComponent(); + if (component == null) { + return false; + } + String componentName = component.flattenToString(); + final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1"; + Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, + new String[]{"intent", "restored"}, where, null, null); + boolean result = false; + try { + result = c.moveToFirst(); + } finally { + c.close(); + } + Log.d(TAG, "shortcutWasRestored is " + result + " for " + componentName); + return result; + } + + /** * Returns an ItemInfo array containing all the items in the LauncherModel. * The ItemInfo.id is not set through this function. */ @@ -819,6 +914,7 @@ public class LauncherModel extends BroadcastReceiver { final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); + final int hiddenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.HIDDEN); FolderInfo folderInfo = null; switch (c.getInt(itemTypeIndex)) { @@ -833,6 +929,7 @@ public class LauncherModel extends BroadcastReceiver { folderInfo.screenId = c.getInt(screenIndex); folderInfo.cellX = c.getInt(cellXIndex); folderInfo.cellY = c.getInt(cellYIndex); + folderInfo.hidden = c.getInt(hiddenIndex) > 0; return folderInfo; } @@ -1017,6 +1114,10 @@ public class LauncherModel extends BroadcastReceiver { * a list of screen ids in the order that they should appear. */ void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) { + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true); + Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true); + final ArrayList<Long> screensCopy = new ArrayList<Long>(screens); final ContentResolver cr = context.getContentResolver(); final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -1033,18 +1134,23 @@ public class LauncherModel extends BroadcastReceiver { Runnable r = new Runnable() { @Override public void run() { + ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); // Clear the table - cr.delete(uri, null, null); + ops.add(ContentProviderOperation.newDelete(uri).build()); int count = screensCopy.size(); - ContentValues[] values = new ContentValues[count]; for (int i = 0; i < count; i++) { ContentValues v = new ContentValues(); long screenId = screensCopy.get(i); v.put(LauncherSettings.WorkspaceScreens._ID, screenId); v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); - values[i] = v; + ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build()); + } + + try { + cr.applyBatch(LauncherProvider.AUTHORITY, ops); + } catch (Exception ex) { + throw new RuntimeException(ex); } - cr.bulkInsert(uri, values); synchronized (sBgLock) { sBgWorkspaceScreens.clear(); @@ -1139,15 +1245,29 @@ public class LauncherModel extends BroadcastReceiver { } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { - // First, schedule to add these apps back in. + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); - // Then, rebind everything. - startLoaderFromBackground(); + if (!replacing) { + enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); + if (mAppsCanBeOnRemoveableStorage) { + // Only rebind if we support removable storage. It catches the case where + // apps on the external sd card need to be reloaded + startLoaderFromBackground(); + } + } else { + // If we are replacing then just update the packages in the list + enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, + packages)); + } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { - String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - enqueuePackageUpdated(new PackageUpdatedTask( - PackageUpdatedTask.OP_UNAVAILABLE, packages)); + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (!replacing) { + String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + enqueuePackageUpdated(new PackageUpdatedTask( + PackageUpdatedTask.OP_UNAVAILABLE, packages)); + } + // else, we are replacing the packages, so ignore this event and wait for + // EXTERNAL_APPLICATIONS_AVAILABLE to update the packages at that time } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); @@ -1211,7 +1331,7 @@ public class LauncherModel extends BroadcastReceiver { } } if (runLoader) { - startLoader(false, -1); + startLoader(false, PagedView.INVALID_RESTORE_PAGE); } } @@ -1230,6 +1350,10 @@ public class LauncherModel extends BroadcastReceiver { } public void startLoader(boolean isLaunching, int synchronousBindPage) { + startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE); + } + + public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); @@ -1244,8 +1368,9 @@ public class LauncherModel extends BroadcastReceiver { // If there is already one running, tell it to stop. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); - mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching); - if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { + mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags); + if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE + && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } else { sWorkerThread.setPriority(Thread.NORM_PRIORITY); @@ -1297,6 +1422,15 @@ public class LauncherModel extends BroadcastReceiver { } finally { sc.close(); } + + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true); + ArrayList<String> orderedScreensPairs= new ArrayList<String>(); + for (Integer i : orderedScreens.keySet()) { + orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }"); + } + Launcher.addDumpLog(TAG, "11683562 - screens: " + + TextUtils.join(", ", orderedScreensPairs), true); return orderedScreens; } @@ -1313,75 +1447,6 @@ public class LauncherModel extends BroadcastReceiver { return false; } - // check & update map of what's occupied; used to discard overlapping/invalid items - public boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item, - AtomicBoolean deleteOnItemOverlap) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - long containerIndex = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { - if (occupied.containsKey((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT)) { - if (occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT) - [(int) item.screenId][0] != null) { - Log.e(TAG, "Error loading shortcut into hotseat " + item - + " into position (" + item.screenId + ":" + item.cellX + "," - + item.cellY + ") occupied by " - + occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT) - [(int) item.screenId][0]); - if (occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT) - [(int) item.screenId][0].itemType == LauncherSettings.Favorites.ITEM_TYPE_ALLAPPS) { - deleteOnItemOverlap.set(true); - } - return false; - } else { - ItemInfo[][] hotseatItems = occupied.get( - (long) LauncherSettings.Favorites.CONTAINER_HOTSEAT); - hotseatItems[(int) item.screenId][0] = item; - return true; - } - } else { - ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1]; - items[(int) item.screenId][0] = item; - occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items); - return true; - } - } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { - // Skip further checking if it is not the hotseat or workspace container - return true; - } - - int countX = (int) grid.numColumns; - int countY = (int) grid.numRows; - - if (!occupied.containsKey(item.screenId)) { - ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1]; - occupied.put(item.screenId, items); - } - - ItemInfo[][] screens = occupied.get(item.screenId); - // Check if any workspace icons overlap with each other - for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { - for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { - if (screens[x][y] != null) { - Log.e(TAG, "Error loading shortcut " + item - + " into cell (" + containerIndex + "-" + item.screenId + ":" - + x + "," + y - + ") occupied by " - + screens[x][y]); - return false; - } - } - } - for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { - for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { - screens[x][y] = item; - } - } - - return true; - } - /** * Runnable for the thread that loads the contents of the launcher: * - workspace icons @@ -1394,13 +1459,15 @@ public class LauncherModel extends BroadcastReceiver { private boolean mIsLoadingAndBindingWorkspace; private boolean mStopped; private boolean mLoadAndBindStepFinished; + private int mFlags; private HashMap<Object, CharSequence> mLabelCache; - LoaderTask(Context context, boolean isLaunching) { + LoaderTask(Context context, boolean isLaunching, int flags) { mContext = context; mIsLaunching = isLaunching; mLabelCache = new HashMap<Object, CharSequence>(); + mFlags = flags; } boolean isLaunching() { @@ -1473,7 +1540,7 @@ public class LauncherModel extends BroadcastReceiver { } void runBindSynchronousPage(int synchronousBindPage) { - if (synchronousBindPage < 0) { + if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) { // Ensure that we have a valid page index to load synchronously throw new RuntimeException("Should not call runBindSynchronousPage() without " + "valid page index"); @@ -1562,7 +1629,7 @@ public class LauncherModel extends BroadcastReceiver { sBgDbIconCache.clear(); } - if (AppsCustomizePagedView.DISABLE_ALL_APPS) { + if (LauncherAppState.isDisableAllApps()) { // Ensure that all the applications that are in the system are // represented on the home screen. if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) { @@ -1638,16 +1705,90 @@ public class LauncherModel extends BroadcastReceiver { } } if (!added.isEmpty()) { - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; - addAndBindAddedApps(context, added, cb, null); + addAndBindAddedWorkspaceApps(context, added); } } - private boolean checkItemDimensions(ItemInfo info) { + private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item, + AtomicBoolean deleteOnInvalidPlacement) { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - return (info.cellX + info.spanX) > (int) grid.numColumns || - (info.cellY + info.spanY) > (int) grid.numRows; + final int countX = (int) grid.numColumns; + final int countY = (int) grid.numRows; + long containerIndex = item.screenId; + if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { + // Return early if we detect that an item is under the hotseat button + if (mCallbacks == null) { + deleteOnInvalidPlacement.set(true); + Log.e(TAG, "Error loading shortcut into hotseat " + item + + " into position (" + item.screenId + ":" + item.cellX + "," + + item.cellY + ") occupied by all apps"); + return false; + } + final ItemInfo[][] hotseatItems = + occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT); + if (item.screenId >= grid.numHotseatIcons) { + Log.e(TAG, "Error loading shortcut " + item + + " into hotseat position " + item.screenId + + ", position out of bounds: (0 to " + (grid.numHotseatIcons - 1) + + ")"); + return false; + } + if (hotseatItems != null) { + if (hotseatItems[(int) item.screenId][0] != null) { + Log.e(TAG, "Error loading shortcut into hotseat " + item + + " into position (" + item.screenId + ":" + item.cellX + "," + + item.cellY + ") occupied by " + + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT) + [(int) item.screenId][0]); + return false; + } else { + hotseatItems[(int) item.screenId][0] = item; + return true; + } + } else { + final ItemInfo[][] items = new ItemInfo[(int) grid.numHotseatIcons][1]; + items[(int) item.screenId][0] = item; + occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items); + return true; + } + } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + // Skip further checking if it is not the hotseat or workspace container + return true; + } + if (!occupied.containsKey(item.screenId)) { + ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1]; + occupied.put(item.screenId, items); + } + final ItemInfo[][] screens = occupied.get(item.screenId); + if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && + item.cellX < 0 || item.cellY < 0 || + item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { + Log.e(TAG, "Error loading shortcut " + item + + " into cell (" + containerIndex + "-" + item.screenId + ":" + + item.cellX + "," + item.cellY + + ") out of screen bounds ( " + countX + "x" + countY + ")"); + return false; + } + // Check if any workspace icons overlap with each other + for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { + for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { + if (screens[x][y] != null) { + Log.e(TAG, "Error loading shortcut " + item + + " into cell (" + containerIndex + "-" + item.screenId + ":" + + x + "," + y + + ") occupied by " + + screens[x][y]); + return false; + } + } + } + for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { + for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { + screens[x][y] = item; + } + } + return true; } /** Clears all the sBg data structures */ @@ -1662,8 +1803,11 @@ public class LauncherModel extends BroadcastReceiver { } } - /** Returns whether this is an upgradge path */ + /** Returns whether this is an upgrade path */ private boolean loadWorkspace() { + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true); + final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; final Context context = mContext; @@ -1677,16 +1821,33 @@ public class LauncherModel extends BroadcastReceiver { int countX = (int) grid.numColumns; int countY = (int) grid.numRows; - // Make sure the default workspace is loaded, if needed - LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0); + if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) { + Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true); + LauncherAppState.getLauncherProvider().deleteDatabase(); + } + + if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) { + // append the user's Launcher2 shortcuts + Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true); + LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts(); + } else { + // Make sure the default workspace is loaded + Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false); + LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0); + } // Check if we need to do any upgrade-path logic + // (Includes having just imported default favorites) boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb(); + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true); + synchronized (sBgLock) { clearSBgDataStructures(); final ArrayList<Long> itemsToRemove = new ArrayList<Long>(); + final ArrayList<Long> restoredRows = new ArrayList<Long>(); final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI; if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri); final Cursor c = contentResolver.query(contentUri, null, null, null, null); @@ -1727,8 +1888,11 @@ public class LauncherModel extends BroadcastReceiver { (LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.SPANY); - //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - //final int displayModeIndex = c.getColumnIndexOrThrow( + final int restoredIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.RESTORED); + final int hiddenIndex = c.getColumnIndexOrThrow( + LauncherSettings.Favorites.HIDDEN); + //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); //final int displayModeIndex = c.getColumnIndexOrThrow( // LauncherSettings.Favorites.DISPLAY_MODE); ShortcutInfo info; @@ -1739,9 +1903,10 @@ public class LauncherModel extends BroadcastReceiver { Intent intent = null; while (!mStopped && c.moveToNext()) { - AtomicBoolean deleteOnItemOverlap = new AtomicBoolean(false); + AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false); try { int itemType = c.getInt(itemTypeIndex); + boolean restored = 0 != c.getInt(restoredIndex); switch (itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: @@ -1754,25 +1919,45 @@ public class LauncherModel extends BroadcastReceiver { intent = Intent.parseUri(intentDescription, 0); ComponentName cn = intent.getComponent(); if (cn != null && !isValidPackageComponent(manager, cn)) { - if (!mAppsCanBeOnRemoveableStorage) { - // Log the invalid package, and remove it from the db - Launcher.addDumpLog(TAG, "Invalid package removed: " + cn, true); - itemsToRemove.add(id); + if (restored) { + // might be installed later + Launcher.addDumpLog(TAG, + "package not yet restored: " + cn, true); } else { - // If apps can be on external storage, then we just - // leave them for the user to remove (maybe add - // visual treatment to it) - Launcher.addDumpLog(TAG, "Invalid package found: " + cn, true); + if (!mAppsCanBeOnRemoveableStorage) { + // Log the invalid package, and remove it + Launcher.addDumpLog(TAG, + "Invalid package removed: " + cn, true); + itemsToRemove.add(id); + } else { + // If apps can be on external storage, then we just + // leave them for the user to remove (maybe add + // visual treatment to it) + Launcher.addDumpLog(TAG, + "Invalid package found: " + cn, true); + } + continue; } - continue; + } else if (restored) { + // no special handling necessary for this restored item + restoredRows.add(id); + restored = false; } } catch (URISyntaxException e) { - Launcher.addDumpLog(TAG, "Invalid uri: " + intentDescription, true); + Launcher.addDumpLog(TAG, + "Invalid uri: " + intentDescription, true); continue; } } - if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + if (restored) { + Launcher.addDumpLog(TAG, + "constructing info for partially restored package", + true); + info = getRestoredItemInfo(c, titleIndex, intent); + intent = getRestoredItemIntent(c, context, intent); + } else if (itemType == + LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { info = getShortcutInfo(manager, intent, context, c, iconIndex, titleIndex, mLabelCache); } else if (itemType == LauncherSettings.Favorites.ITEM_TYPE_ALLAPPS) { @@ -1806,18 +1991,11 @@ public class LauncherModel extends BroadcastReceiver { info.cellY = c.getInt(cellYIndex); info.spanX = 1; info.spanY = 1; - // Skip loading items that are out of bounds - if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - if (checkItemDimensions(info)) { - Launcher.addDumpLog(TAG, "Skipped loading out of bounds shortcut: " - + info + ", " + grid.numColumns + "x" + grid.numRows, true); - continue; - } - } + // check & update map of what's occupied - deleteOnItemOverlap.set(false); - if (!checkItemPlacement(occupied, info, deleteOnItemOverlap)) { - if (deleteOnItemOverlap.get()) { + deleteOnInvalidPlacement.set(false); + if (!checkItemPlacement(occupied, info, deleteOnInvalidPlacement)) { + if (deleteOnInvalidPlacement.get()) { itemsToRemove.add(id); } break; @@ -1858,19 +2036,13 @@ public class LauncherModel extends BroadcastReceiver { folderInfo.cellY = c.getInt(cellYIndex); folderInfo.spanX = 1; folderInfo.spanY = 1; + folderInfo.hidden = c.getInt(hiddenIndex) > 0; - // Skip loading items that are out of bounds - if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - if (checkItemDimensions(folderInfo)) { - Log.d(TAG, "Skipped loading out of bounds folder"); - continue; - } - } // check & update map of what's occupied - deleteOnItemOverlap.set(false); + deleteOnInvalidPlacement.set(false); if (!checkItemPlacement(occupied, folderInfo, - deleteOnItemOverlap)) { - if (deleteOnItemOverlap.get()) { + deleteOnInvalidPlacement)) { + if (deleteOnInvalidPlacement.get()) { itemsToRemove.add(id); } break; @@ -1883,6 +2055,11 @@ public class LauncherModel extends BroadcastReceiver { break; } + if (restored) { + // no special handling required for restored folders + restoredRows.add(id); + } + sBgItemsIdMap.put(folderInfo.id, folderInfo); sBgFolders.put(folderInfo.id, folderInfo); break; @@ -1926,18 +2103,11 @@ public class LauncherModel extends BroadcastReceiver { } appWidgetInfo.container = c.getInt(containerIndex); - // Skip loading items that are out of bounds - if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - if (checkItemDimensions(appWidgetInfo)) { - Log.d(TAG, "Skipped loading out of bounds app widget"); - continue; - } - } // check & update map of what's occupied - deleteOnItemOverlap.set(false); + deleteOnInvalidPlacement.set(false); if (!checkItemPlacement(occupied, appWidgetInfo, - deleteOnItemOverlap)) { - if (deleteOnItemOverlap.get()) { + deleteOnInvalidPlacement)) { + if (deleteOnInvalidPlacement.get()) { itemsToRemove.add(id); } break; @@ -1957,7 +2127,7 @@ public class LauncherModel extends BroadcastReceiver { break; } } catch (Exception e) { - Launcher.addDumpLog(TAG, "Desktop items loading interrupted: " + e, true); + Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true); } } } finally { @@ -2003,6 +2173,25 @@ public class LauncherModel extends BroadcastReceiver { } } + if (restoredRows.size() > 0) { + ContentProviderClient updater = contentResolver.acquireContentProviderClient( + LauncherSettings.Favorites.CONTENT_URI); + // Update restored items that no longer require special handling + try { + StringBuilder selectionBuilder = new StringBuilder(); + selectionBuilder.append(LauncherSettings.Favorites._ID); + selectionBuilder.append(" IN ("); + selectionBuilder.append(TextUtils.join(", ", restoredRows)); + selectionBuilder.append(")"); + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites.RESTORED, 0); + updater.update(LauncherSettings.Favorites.CONTENT_URI, + values, selectionBuilder.toString(), null); + } catch (RemoteException e) { + Log.w(TAG, "Could not update restored rows"); + } + } + if (loadedOldDb) { long maxScreenId = 0; // If we're importing we use the old screen order. @@ -2017,6 +2206,10 @@ public class LauncherModel extends BroadcastReceiver { } } Collections.sort(sBgWorkspaceScreens); + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true); + Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + + TextUtils.join(", ", sBgWorkspaceScreens), true); LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId); updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); @@ -2033,6 +2226,9 @@ public class LauncherModel extends BroadcastReceiver { for (Integer i : orderedScreens.keySet()) { sBgWorkspaceScreens.add(orderedScreens.get(i)); } + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + + TextUtils.join(", ", sBgWorkspaceScreens), true); // Remove any empty screens ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens); @@ -2046,6 +2242,10 @@ public class LauncherModel extends BroadcastReceiver { // If there are any empty screens remove them, and update. if (unusedScreens.size() != 0) { + // Log to disk + Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " + + TextUtils.join(", ", unusedScreens), true); + sBgWorkspaceScreens.removeAll(unusedScreens); updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); } @@ -2077,7 +2277,7 @@ public class LauncherModel extends BroadcastReceiver { /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ - private void filterCurrentWorkspaceItems(int currentScreen, + private void filterCurrentWorkspaceItems(long currentScreenId, ArrayList<ItemInfo> allWorkspaceItems, ArrayList<ItemInfo> currentScreenItems, ArrayList<ItemInfo> otherScreenItems) { @@ -2090,12 +2290,6 @@ public class LauncherModel extends BroadcastReceiver { } } - // If we aren't filtering on a screen, then the set of items to load is the full set of - // items given. - if (currentScreen < 0) { - currentScreenItems.addAll(allWorkspaceItems); - } - // Order the set of items by their containers first, this allows use to walk through the // list sequentially, build up a list of containers that are in the specified screen, // as well as all items in those containers. @@ -2108,7 +2302,7 @@ public class LauncherModel extends BroadcastReceiver { }); for (ItemInfo info : allWorkspaceItems) { if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - if (info.screenId == currentScreen) { + if (info.screenId == currentScreenId) { currentScreenItems.add(info); itemsOnScreen.add(info.id); } else { @@ -2129,20 +2323,15 @@ public class LauncherModel extends BroadcastReceiver { } /** Filters the set of widgets which are on the specified screen. */ - private void filterCurrentAppWidgets(int currentScreen, + private void filterCurrentAppWidgets(long currentScreenId, ArrayList<LauncherAppWidgetInfo> appWidgets, ArrayList<LauncherAppWidgetInfo> currentScreenWidgets, ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) { - // If we aren't filtering on a screen, then the set of items to load is the full set of - // widgets given. - if (currentScreen < 0) { - currentScreenWidgets.addAll(appWidgets); - } for (LauncherAppWidgetInfo widget : appWidgets) { if (widget == null) continue; if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - widget.screenId == currentScreen) { + widget.screenId == currentScreenId) { currentScreenWidgets.add(widget); } else { otherScreenWidgets.add(widget); @@ -2151,23 +2340,18 @@ public class LauncherModel extends BroadcastReceiver { } /** Filters the set of folders which are on the specified screen. */ - private void filterCurrentFolders(int currentScreen, + private void filterCurrentFolders(long currentScreenId, HashMap<Long, ItemInfo> itemsIdMap, HashMap<Long, FolderInfo> folders, HashMap<Long, FolderInfo> currentScreenFolders, HashMap<Long, FolderInfo> otherScreenFolders) { - // If we aren't filtering on a screen, then the set of items to load is the full set of - // widgets given. - if (currentScreen < 0) { - currentScreenFolders.putAll(folders); - } for (long id : folders.keySet()) { ItemInfo info = itemsIdMap.get(id); FolderInfo folder = folders.get(id); if (info == null || folder == null) continue; if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - info.screenId == currentScreen) { + info.screenId == currentScreenId) { currentScreenFolders.put(id, folder); } else { otherScreenFolders.put(id, folder); @@ -2220,8 +2404,11 @@ public class LauncherModel extends BroadcastReceiver { ArrayList<ComponentName> mHiddenApps = new ArrayList<ComponentName>(); ArrayList<String> mHiddenAppsPackages = new ArrayList<String>(); Context context = mApp.getContext(); - String[] flattened = SettingsProvider.getStringCustomDefault(context, - SettingsProvider.SETTINGS_UI_DRAWER_HIDDEN_APPS, "").split("\\|"); + // Since Trebuchet is compiled using the SDK we have to hardcode this string + String protectedComponents = Settings.Secure.getString(context.getContentResolver(), + SETTINGS_PROTECTED_COMPONENTS); + protectedComponents = protectedComponents == null ? "" : protectedComponents; + String[] flattened = protectedComponents.split("\\|"); boolean hideShortcuts = SettingsProvider.getBoolean(context, SettingsProvider.SETTINGS_UI_DRAWER_REMOVE_HIDDEN_APPS_SHORTCUTS, R.bool.preferences_interface_drawer_remove_hidden_apps_shortcuts_default); @@ -2251,20 +2438,30 @@ public class LauncherModel extends BroadcastReceiver { } } } else { + // Only remove items from folders that aren't hidden final FolderInfo folder = (FolderInfo)item; List<ShortcutInfo> shortcuts = folder.contents; + int NN = shortcuts.size() - 1; for (int j = NN; j >= 0; j--) { ShortcutInfo sci = shortcuts.get(j); if (sci.intent != null && sci.intent.getComponent() != null) { - if (mHiddenApps.contains(sci.intent.getComponent())) { - LauncherModel.deleteItemFromDatabase(mContext, sci); - folder.remove(sci); + if (!folder.hidden){ + if (mHiddenApps.contains(sci.intent.getComponent())) { + LauncherModel.deleteItemFromDatabase(mContext, sci); + folder.remove(sci); + } + } else { + if (!mHiddenApps.contains(sci.intent.getComponent())) { + LauncherModel.deleteItemFromDatabase(mContext, sci); + folder.remove(sci); + } } + } } - if (folder.contents.size() == 1 /*&& !(folder instanceof LiveFolderInfo)*/) { + if (folder.contents.size() == 1 && !folder.hidden) { ShortcutInfo finalItem = folder.contents.get(0); finalItem.container = folder.container; LauncherModel.deleteItemFromDatabase(mContext, folder); @@ -2382,13 +2579,7 @@ public class LauncherModel extends BroadcastReceiver { return; } - final boolean isLoadingSynchronously = (synchronizeBindPage > -1); - final int currentScreen = isLoadingSynchronously ? synchronizeBindPage : - oldCallbacks.getCurrentWorkspaceScreen(); - - // Load all the items that are on the current page first (and in the process, unbind - // all the existing workspace items before we call startBinding() below. - unbindWorkspaceItemsOnMainThread(); + // Save a copy of all the bg-thread collections ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<LauncherAppWidgetInfo>(); @@ -2403,6 +2594,23 @@ public class LauncherModel extends BroadcastReceiver { orderedScreenIds.addAll(sBgWorkspaceScreens); } + final boolean isLoadingSynchronously = + synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE; + int currScreen = isLoadingSynchronously ? synchronizeBindPage : + oldCallbacks.getCurrentWorkspaceScreen(); + if (currScreen >= orderedScreenIds.size()) { + // There may be no workspace screens (just hotseat items and an empty page). + currScreen = PagedView.INVALID_RESTORE_PAGE; + } + final int currentScreen = currScreen; + final long currentScreenId = currentScreen < 0 + ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen); + + // Load all the items that are on the current page first (and in the process, unbind + // all the existing workspace items before we call startBinding() below. + unbindWorkspaceItemsOnMainThread(); + + // Separate the items that are on the current screen, and all the other remaining items ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> currentAppWidgets = @@ -2412,12 +2620,11 @@ public class LauncherModel extends BroadcastReceiver { HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>(); HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>(); - // Separate the items that are on the current screen, and all the other remaining items - filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems, + filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); - filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets, + filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets); - filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders, + filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders, otherFolders); sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems); @@ -2442,7 +2649,7 @@ public class LauncherModel extends BroadcastReceiver { r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); - if (callbacks != null) { + if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) { callbacks.onPageBoundSynchronously(currentScreen); } } @@ -2643,6 +2850,7 @@ public class LauncherModel extends BroadcastReceiver { case OP_ADD: for (int i=0; i<N; i++) { if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); + mIconCache.remove(packages[i]); mBgAllAppsList.addPackage(context, packages[i]); } break; @@ -2690,14 +2898,14 @@ public class LauncherModel extends BroadcastReceiver { if (added != null) { // Ensure that we add all the workspace applications to the db - Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; - if (!AppsCustomizePagedView.DISABLE_ALL_APPS) { - addAndBindAddedApps(context, new ArrayList<ItemInfo>(), cb, added); - } else { + if (LauncherAppState.isDisableAllApps()) { final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added); - addAndBindAddedApps(context, addedInfos, cb, added); + addAndBindAddedWorkspaceApps(context, addedInfos); + } else { + addAppsToAllApps(context, added); } } + if (modified != null) { final ArrayList<AppInfo> modifiedFinal = modified; @@ -2723,43 +2931,47 @@ public class LauncherModel extends BroadcastReceiver { } }); } - // If a package has been removed, or an app has been removed as a result of - // an update (for example), make the removed callback. - if (mOp == OP_REMOVE || !removedApps.isEmpty()) { - final boolean packageRemoved = (mOp == OP_REMOVE); - final ArrayList<String> removedPackageNames = - new ArrayList<String>(Arrays.asList(packages)); - - // Update the launcher db to reflect the removal of apps - if (packageRemoved) { - for (String pn : removedPackageNames) { - ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn); - for (ItemInfo i : infos) { - deleteItemFromDatabase(context, i); - } - } - // Remove any queued items from the install queue - String spKey = LauncherAppState.getSharedPreferencesKey(); - SharedPreferences sp = - context.getSharedPreferences(spKey, Context.MODE_PRIVATE); - InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames); - } else { - for (AppInfo a : removedApps) { - ArrayList<ItemInfo> infos = - getItemInfoForComponentName(a.componentName); - for (ItemInfo i : infos) { - deleteItemFromDatabase(context, i); - } + final ArrayList<String> removedPackageNames = + new ArrayList<String>(); + if (mOp == OP_REMOVE) { + // Mark all packages in the broadcast to be removed + removedPackageNames.addAll(Arrays.asList(packages)); + } else if (mOp == OP_UPDATE) { + // Mark disabled packages in the broadcast to be removed + final PackageManager pm = context.getPackageManager(); + for (int i=0; i<N; i++) { + if (isPackageDisabled(pm, packages[i])) { + removedPackageNames.add(packages[i]); } } - + } + // Remove all the components associated with this package + for (String pn : removedPackageNames) { + ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn); + for (ItemInfo i : infos) { + deleteItemFromDatabase(context, i); + } + } + // Remove all the specific components + for (AppInfo a : removedApps) { + ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName); + for (ItemInfo i : infos) { + deleteItemFromDatabase(context, i); + } + } + if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) { + // Remove any queued items from the install queue + String spKey = LauncherAppState.getSharedPreferencesKey(); + SharedPreferences sp = + context.getSharedPreferences(spKey, Context.MODE_PRIVATE); + InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames); + // Call the components-removed callback mHandler.post(new Runnable() { public void run() { Callbacks cb = mCallbacks != null ? mCallbacks.get() : null; if (callbacks == cb && cb != null) { - callbacks.bindComponentsRemoved(removedPackageNames, - removedApps, packageRemoved); + callbacks.bindComponentsRemoved(removedPackageNames, removedApps); } } }); @@ -2801,19 +3013,27 @@ public class LauncherModel extends BroadcastReceiver { return widgetsAndShortcuts; } - private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) { + private static boolean isPackageDisabled(PackageManager pm, String packageName) { + try { + PackageInfo pi = pm.getPackageInfo(packageName, 0); + return !pi.applicationInfo.enabled; + } catch (NameNotFoundException e) { + // Fall through + } + return false; + } + + public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) { if (cn == null) { return false; } + if (isPackageDisabled(pm, cn.getPackageName())) { + return false; + } try { - // Skip if the application is disabled - PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0); - if (!pi.applicationInfo.enabled) { - return false; - } - // Check the activity + PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0); return (pm.getActivityInfo(cn, 0) != null); } catch (NameNotFoundException e) { return false; @@ -2821,6 +3041,41 @@ public class LauncherModel extends BroadcastReceiver { } /** + * Make an ShortcutInfo object for a restored application or shortcut item that points + * to a package that is not yet installed on the system. + */ + public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) { + final ShortcutInfo info = new ShortcutInfo(); + if (cursor != null) { + info.title = cursor.getString(titleIndex); + } else { + info.title = ""; + } + info.setIcon(mIconCache.getIcon(intent, info.title.toString())); + info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; + info.restoredIntent = intent; + return info; + } + + /** + * Make an Intent object for a restored application or shortcut item that points + * to the market page for the item. + */ + private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) { + final boolean debug = false; + ComponentName componentName = intent.getComponent(); + Intent marketIntent = new Intent(Intent.ACTION_VIEW); + Uri marketUri = new Uri.Builder() + .scheme("market") + .authority("details") + .appendQueryParameter("id", componentName.getPackageName()) + .build(); + if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString()); + marketIntent.setData(marketUri); + return marketIntent; + } + + /** * This is called from the code that adds shortcuts from the intent receiver. This * doesn't have a Cursor, but */ @@ -2983,6 +3238,10 @@ public class LauncherModel extends BroadcastReceiver { Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { return true; } + // placeholder shortcuts get special treatment, let them through too. + if (info.getRestoredIntent() != null) { + return true; + } } return false; } |