import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
+import cyanogenmod.providers.CMSettings;
+
+import com.android.launcher3.settings.SettingsProvider;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
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;
+ public static final int LOADER_FLAG_RESIZE_GRID = 1 << 2;
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private static final long INVALID_SCREEN_ID = -1L;
@Thunk boolean mIsLoaderTaskRunning;
@Thunk boolean mHasLoaderCompletedOnce;
+ private volatile boolean mFlushingWorkerThread;
+
private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
+ public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
public void bindAllPackages(WidgetsModel model);
public void bindSearchProviderChanged();
+ public void bindComponentsUnavailable(ArrayList<String> packageNames,
+ ArrayList<AppInfo> appInfos);
+ public void bindComponentsAvailable(ArrayList<ItemInfo> itemInfos);
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
public void dumpLogsToLocalData();
ArrayList<Long> workspaceScreens,
ArrayList<Long> addedWorkspaceScreensFinal,
int spanX, int spanY) {
+
+ // Preferred screen is the next one after the default.
+ long preferredScreenId = SettingsProvider.getLongCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ R.integer.preferences_interface_homescreen_id_default);
+ int preferredScreenIndex = 0;
+ for (int i = 0; i < workspaceScreens.size(); i++) {
+ if (workspaceScreens.get(i) == preferredScreenId) {
+ preferredScreenIndex = i + 1;
+ break;
+ }
+ }
+
+ return findSpaceForItem(context, workspaceScreens, addedWorkspaceScreensFinal,
+ spanX, spanY, preferredScreenIndex);
+ }
+
+ /**
+ * Find a position on the screen for the given size or adds a new screen. Checks
+ * preferredScreen first, and if no space is found then starts searching from the left.
+ * @return screenId and the coordinates for the item.
+ */
+ @Thunk Pair<Long, int[]> findSpaceForItem(
+ Context context,
+ ArrayList<Long> workspaceScreens,
+ ArrayList<Long> addedWorkspaceScreensFinal,
+ int spanX, int spanY, int preferredScreenIndex) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
}
}
+ // If we have a zero-id screen then we skip over it.
+ boolean hasZero = false;
+ if (!workspaceScreens.isEmpty() && workspaceScreens.get(0) == 0) {
+ hasZero = true;
+ }
+
// Find appropriate space for the item.
long screenId = 0;
int[] cordinates = new int[2];
int screenCount = workspaceScreens.size();
// First check the preferred screen.
- int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
if (preferredScreenIndex < screenCount) {
screenId = workspaceScreens.get(preferredScreenIndex);
found = findNextAvailableIconSpaceInScreen(
if (!found) {
// Search on any of the screens starting from the first screen.
- for (int screen = 1; screen < screenCount; screen++) {
+ int firstScreen = hasZero ? 1 : 0;
+ for (int screen = firstScreen; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (findNextAvailableIconSpaceInScreen(
screenItems.get(screenId), cordinates, spanX, spanY)) {
modelShortcut.spanX == shortcut.spanX &&
modelShortcut.spanY == shortcut.spanY &&
((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
- (modelShortcut.dropPos != null &&
- shortcut.dropPos != null &&
- modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
- modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
+ (modelShortcut.dropPos != null &&
+ shortcut.dropPos != null &&
+ modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
+ modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
// For all intents and purposes, this is the same object
return;
}
- }
- // the modelItem needs to match up perfectly with item if our model is
- // to be consistent with the database-- for now, just require
- // modelItem == item or the equality check above
- String msg = "item: " + ((item != null) ? item.toString() : "null") +
- "modelItem: " +
- ((modelItem != null) ? modelItem.toString() : "null") +
- "Error: ItemInfo passed to checkItemInfo doesn't match original";
- RuntimeException e = new RuntimeException(msg);
- if (stackTrace != null) {
- e.setStackTrace(stackTrace);
+ // the modelItem needs to match up perfectly with item if our model is
+ // to be consistent with the database-- for now, just require
+ // modelItem == item or the equality check above
+ String msg = "item: " + ((item != null) ? item.toString() : "null") +
+ "modelItem: " +
+ ((modelItem != null) ? modelItem.toString() : "null") +
+ "Error: ItemInfo passed to checkItemInfo doesn't match original";
+ RuntimeException e = new RuntimeException(msg);
+ if (stackTrace != null) {
+ e.setStackTrace(stackTrace);
+ }
+ throw e;
}
- throw e;
}
}
}
}
+ public void flushWorkerThread() {
+ mFlushingWorkerThread = true;
+ Runnable waiter = new Runnable() {
+ public void run() {
+ synchronized (this) {
+ notifyAll();
+ mFlushingWorkerThread = false;
+ }
+ }
+ };
+
+ synchronized(waiter) {
+ runOnWorkerThread(waiter);
+ if (mLoaderTask != null) {
+ synchronized(mLoaderTask) {
+ mLoaderTask.notify();
+ }
+ }
+ boolean success = false;
+ while (!success) {
+ try {
+ waiter.wait();
+ success = true;
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
/**
* Move an item in the DB to a new <container, screen, cellX, cellY>
*/
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
+ final int hiddenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.HIDDEN);
+ final int subType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
FolderInfo folderInfo = null;
switch (c.getInt(itemTypeIndex)) {
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.options = c.getInt(optionsIndex);
+ folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = subType;
return folderInfo;
}
}
/**
+ * Saves the total widget count to a shared preference
+ *
+ * @param context {@link Context}
+ */
+ /* package */ static void saveWidgetCount(Context context) {
+ int widgetCount = LauncherModel.sBgAppWidgets.size();
+ SharedPreferences prefs = context.getSharedPreferences(LauncherAppState
+ .getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ }
+
+ /**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
*/
- public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
+ public static void addItemToDatabase(final Context context, final ItemInfo item, final long container,
final long screenId, final int cellX, final int cellY) {
item.container = container;
item.cellX = cellX;
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sBgAppWidgets.add((LauncherAppWidgetInfo) item);
+ saveWidgetCount(context);
break;
}
}
/**
* Removes the specified items from the database
* @param context
- * @param item
+ * @param items
*/
- static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
+ static void deleteItemsFromDatabase(final Context context, final ArrayList<? extends ItemInfo> items) {
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
public void run() {
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
+ saveWidgetCount(context);
break;
}
sBgItemsIdMap.remove(item.id);
}
/**
+ * Saves the count of workspace pages
+ *
+ * @param context {@link Context}
+ */
+ /* package */ static void savePageCount(Context context) {
+ int pageCount = LauncherModel.sBgWorkspaceScreens.size();
+ SharedPreferences prefs = context.getSharedPreferences(LauncherAppState
+ .getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ }
+
+ /**
* Update the order of the workspace screens in the database. The array list contains
* a list of screen ids in the order that they should appear.
*/
- public void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+ public void updateWorkspaceScreenOrder(final Context context, final ArrayList<Long> screens) {
final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
final ContentResolver cr = context.getContentResolver();
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
synchronized (sBgLock) {
sBgWorkspaceScreens.clear();
sBgWorkspaceScreens.addAll(screensCopy);
+ savePageCount(context);
}
}
};
// check & update map of what's occupied; used to discard overlapping/invalid items
private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
- ArrayList<Long> workspaceScreens) {
+ ArrayList<Long> workspaceScreens, boolean shouldResizeAndUpdateDB) {
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
final int countX = profile.numColumns;
return true;
}
- if (!occupied.containsKey(item.screenId)) {
- ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
- occupied.put(item.screenId, items);
+ // If the current item's position lies outside of the bounds
+ // of the current grid size, attempt to place it in the next
+ // available position.
+ if (item.cellX < 0 || item.cellY < 0 || item.cellX + item.spanX > countX
+ || item.cellY + item.spanY > countY) {
+ // If we won't be resizing the grid, then just return, this item does not fit.
+ if (!shouldResizeAndUpdateDB) {
+ 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;
+ }
+
+ if (item.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+ // Place the item at 0 0 of screen 1
+ // if items overlap here, they will be moved later on
+ item.cellX = 0;
+ item.cellY = 0;
+ item.screenId = 1;
+ item.wasMovedDueToReducedSpace = true;
+ item.requiresDbUpdate = true;
+ } else {
+ // see if widget can be shrunk to fit a screen, if not, just remove it
+ if (item.minSpanX > countX || item.minSpanY > countY) {
+ return false;
+ }
+ // if the widget is larger than the grid, shrink it down
+ if (item.cellX + item.spanX > countX) {
+ item.cellX = 0;
+ item.spanY = (item.spanY / 2) > 0 ? item.spanY / 2 : 1;
+ item.spanX = item.minSpanX;
+ item.requiresDbUpdate = true;
+ item.wasMovedDueToReducedSpace = true;
+ }
+ if (item.cellY + item.spanY > countY) {
+ item.cellY = 0;
+ item.spanY = countY;
+ item.requiresDbUpdate = true;
+ item.wasMovedDueToReducedSpace = true;
+ }
+ if (item.cellY + item.spanY == countY && item.cellX + item.spanX == countX) {
+ // if the widget is the size of the grid, make a screen all it's own.
+ item.screenId = sBgWorkspaceScreens.size() + 1;
+ }
+ }
+ } else {
+ item.wasMovedDueToReducedSpace = false;
+ item.requiresDbUpdate = true;
}
- 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;
+ if (!occupied.containsKey(item.screenId)) {
+ ItemInfo[][] items = new ItemInfo[countX][countY];
+ 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;
+ if (!shouldResizeAndUpdateDB) {
+ Log.e(TAG, "Error loading shortcut " + item
+ + " into cell (" + containerIndex + "-" + item.screenId + ":"
+ + x + "," + y
+ + ") occupied by "
+ + screens[x][y]);
+ return false;
+ }
+ ItemInfo occupiedItem = screens[x][y];
+ // If an item is overlapping another because one of them
+ // was moved due to the size of the grid changing,
+ // move the current item to a free spot past this one.
+ if (occupiedItem.wasMovedDueToReducedSpace
+ || item.wasMovedDueToReducedSpace) {
+ // overlapping icon exists here
+ // we must find a free space.
+ boolean freeFound = false;
+ int nextX = 0;
+ int nextY = 0;
+ while (!freeFound) {
+ if (screens[nextX][nextY] == null) {
+ item.cellX = nextX;
+ item.cellY = nextY;
+ freeFound = true;
+ } else {
+ if (nextX + item.spanX == countX) {
+ if (nextY + item.spanY == countY) {
+ // If we've reached the bottom of the page and are still
+ // searching, add a new page to place this item.
+ item.screenId += 1;
+ nextY = 0;
+ nextX = 0;
+ if (!occupied.containsKey(item.screenId)) {
+ ItemInfo[][] items = new ItemInfo[countX][countY];
+ occupied.put(item.screenId, items);
+ }
+ screens = occupied.get(item.screenId);
+ } else {
+ nextX = 0;
+ nextY++;
+ }
+ } else {
+ nextX++;
+ }
+ }
+ }
+ }
}
}
}
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;
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+ && shouldResizeAndUpdateDB) {
+ // fill up the entire grid where the widget technically is
+ for (int spanX = x; spanX < item.spanX; spanX++) {
+ screens[spanX][y] = item;
+ for (int spanY = y; spanY < item.spanX; spanY++) {
+ screens[spanX][spanY] = item;
+ }
+ }
+ }
}
}
int countX = profile.numColumns;
int countY = profile.numRows;
+ boolean shouldResize = ((mFlags & LOADER_FLAG_RESIZE_GRID) != 0);
+
if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) {
long migrationStartTime = System.currentTimeMillis();
Log.v(TAG, "Starting workspace migration after restore");
LauncherSettings.Favorites.PROFILE_ID);
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
+ final int hiddenIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.HIDDEN);
+ final int subTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
}
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens,
+ shouldResize)) {
itemsToRemove.add(id);
break;
}
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
+ folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = c.getInt(subTypeIndex);
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens,
+ shouldResize)) {
itemsToRemove.add(id);
break;
}
appWidgetInfo.container = container;
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, appWidgetInfo,
+ sBgWorkspaceScreens, shouldResize)) {
itemsToRemove.add(id);
break;
}
for (FolderInfo folder : sBgFolders) {
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
int pos = 0;
+ int needIndexing = 0;
for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon) {
+ if (info.usingLowResIcon && pos < FolderIcon.NUM_ITEMS_IN_PREVIEW) {
info.updateIcon(mIconCache, false);
}
pos ++;
- if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
- break;
+ needIndexing += info.screenId + info.cellX + info.cellY;
+ }
+ // If all screenId, cellX, and cellY are 0, then we assume they were all null.
+ if (needIndexing == 0) {
+ synchronized (sBgLock) {
+ int curX = 0;
+ int curY = 0;
+ int folderScreenId = 0;
+ int folderCount = folder.contents.size();
+ Point point = Utilities
+ .caluclateFolderContentDimensions(folderCount, countX, countY);
+ int maxX = point.x;
+ int maxY = point.y;
+ for (ShortcutInfo info : folder.contents) {
+ ItemInfo itemInfo = sBgItemsIdMap.get(info.id);
+ if (curY == maxY) { // New screen
+ curX = 0;
+ curY = 0;
+ folderScreenId++;
+ }
+ itemInfo.screenId = folderScreenId;
+ itemInfo.cellX = curX;
+ itemInfo.cellY = curY;
+ LauncherModel.updateItemInDatabase(context, itemInfo);
+ if (curX == maxX - 1) {
+ curX = 0;
+ curY++;
+ } else {
+ curX++;
+ }
+ }
}
}
}
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
}
+ // If any items have been shifted and require a DB update, update them in the DB.
+ if (shouldResize) {
+ for (ItemInfo info : sBgWorkspaceItems) {
+ if (info != null && info.requiresDbUpdate) {
+ info.requiresDbUpdate = false;
+ LauncherModel.modifyItemInDatabase(mContext, info, info.container,
+ info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
+ }
+ }
+ }
+
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Log.d(TAG, "workspace layout: ");
runOnMainThread(r);
}
+ private void removeHiddenAppsWorkspaceItems(
+ final ArrayList<ItemInfo> workspaceItems,
+ final ArrayList<LauncherAppWidgetInfo> appWidgets,
+ final LongArrayMap<FolderInfo> folders) {
+
+ // Get hidden apps
+ ArrayList<ComponentName> mHiddenApps = new ArrayList<ComponentName>();
+ ArrayList<String> mHiddenAppsPackages = new ArrayList<String>();
+ Context context = mApp.getContext();
+ String protectedComponents = CMSettings.Secure.getString(context.getContentResolver(),
+ CMSettings.Secure.PROTECTED_COMPONENTS);
+ protectedComponents = protectedComponents == null ? "" : protectedComponents;
+ String[] flattened = protectedComponents.split("\\|");
+
+ for (String flat : flattened) {
+ ComponentName cmp = ComponentName.unflattenFromString(flat);
+ if (cmp != null) {
+ mHiddenApps.add(cmp);
+ mHiddenAppsPackages.add(cmp.getPackageName());
+ }
+ }
+
+ // Shortcuts
+ int N = workspaceItems.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ final ItemInfo item = workspaceItems.get(i);
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo shortcut = (ShortcutInfo)item;
+ if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
+ if (mHiddenApps.contains(shortcut.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, shortcut);
+ workspaceItems.remove(i);
+ }
+ }
+ } 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--) {
+ final ShortcutInfo sci = shortcuts.get(j);
+ if (sci.intent != null && sci.intent.getComponent() != null) {
+ if (!folder.hidden){
+ if (mHiddenApps.contains(sci.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, sci);
+ Runnable r = new Runnable() {
+ public void run() {
+ folder.remove(sci);
+ }
+ };
+ runOnMainThread(r);
+ }
+ } else {
+ if (!mHiddenApps.contains(sci.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, sci);
+ Runnable r = new Runnable() {
+ public void run() {
+ folder.remove(sci);
+ }
+ };
+ runOnMainThread(r);
+ }
+ }
+
+ }
+ }
+
+ if (folder.contents.size() == 1 && !folder.hidden) {
+ ShortcutInfo finalItem = folder.contents.get(0);
+ finalItem.container = folder.container;
+ LauncherModel.deleteItemFromDatabase(mContext, folder);
+ // only replace this item back on the workspace if it's not protected
+ // and not a remote folder.
+ if (!mHiddenApps.contains(finalItem.intent.getComponent()) &&
+ !folder.isRemote()) {
+ LauncherModel.addOrMoveItemInDatabase(mContext, finalItem,
+ folder.container, folder.screenId, folder.cellX, folder.cellY);
+ workspaceItems.add(finalItem);
+ }
+ workspaceItems.remove(i);
+ folders.remove(Long.valueOf(item.id));
+
+ // Remote folders are always empty on bind.
+ } else if (folder.contents.size() == 0 && !folder.isRemote()) {
+ LauncherModel.deleteFolderContentsFromDatabase(mContext, folder);
+ workspaceItems.remove(i);
+ folders.remove(Long.valueOf(item.id));
+ }
+ }
+ }
+
+ // AppWidgets
+ N = appWidgets.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ final LauncherAppWidgetInfo item = appWidgets.get(i);
+ if (item.providerName != null) {
+ if (mHiddenAppsPackages.contains(item.providerName.getPackageName())) {
+ LauncherModel.deleteItemFromDatabase(mContext, item);
+ appWidgets.remove(i);
+ }
+ }
+ }
+ }
+
private void bindWorkspaceItems(final Callbacks oldCallbacks,
final ArrayList<ItemInfo> workspaceItems,
final ArrayList<LauncherAppWidgetInfo> appWidgets,
final boolean postOnMainThread = (deferredBindRunnables != null);
+ removeHiddenAppsWorkspaceItems(workspaceItems, appWidgets, folders);
+
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final String[] packages = mPackages;
final int N = packages.length;
+ final ArrayList<String> unavailable = new ArrayList<String>();
switch (mOp) {
case OP_ADD: {
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
mBgAllAppsList.removePackage(packages[i], mUser);
mApp.getWidgetCache().removePackage(packages[i], mUser);
+ if (mOp == OP_UNAVAILABLE) {
+ unavailable.add(packages[i]);
+ }
}
break;
}
new HashMap<ComponentName, AppInfo>();
if (added != null) {
+ final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
addAppsToAllApps(context, added);
for (AppInfo ai : added) {
addedOrUpdatedApps.put(ai.componentName, ai);
}
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks == cb && cb != null) {
+ if (DEBUG_LOADERS) Log.d(TAG, "bindComponentsAvailable: " +
+ addedInfos.size());
+ callbacks.bindComponentsAvailable(addedInfos);
+ }
+ }
+ });
}
if (modified != null) {
final int removeReason;
if (mOp == OP_UNAVAILABLE) {
removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+ // Call the packages-unavailable callback
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks == cb && cb != null) {
+ if (DEBUG_LOADERS) Log.d(TAG, "bindComponentsUnavailable: " +
+ removedApps.size());
+ callbacks.bindComponentsUnavailable(unavailable, removedApps);
+ }
+ }
+ });
} else {
// Remove all the components associated with this package
for (String pn : removedPackageNames) {
// Remove any queued items from the install queue
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
- // Call the components-removed callback
- mHandler.post(new Runnable() {
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindComponentsRemoved(
+ if (mOp == OP_UNAVAILABLE) {
+ // Call the packages-unavailable callback
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks == cb && cb != null) {
+ callbacks.bindComponentsUnavailable(unavailable, removedApps);
+ }
+ }
+ });
+ } else {
+ // Call the components-removed callback
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = getCallback();
+ if (callbacks == cb && cb != null) {
+ callbacks.bindComponentsRemoved(
removedPackageNames, removedApps, mUser, removeReason);
+ }
}
- }
- });
+ });
+ }
}
// Update widgets
// Refresh widget list, if there is any newly added widget
PackageManager pm = context.getPackageManager();
for (String pkg : mPackages) {
- needToRefresh |= !pm.queryBroadcastReceivers(
+ List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(
new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
- .setPackage(pkg), 0).isEmpty();
+ .setPackage(pkg), 0);
+ if (resolveInfos != null) {
+ needToRefresh |= !resolveInfos.isEmpty();
+ }
}
}